feat: Stress tests, benchmarking, and non-failing queues and inboxes #3
Adds some basic benchmarking, stress tests. They still need to be properly examined to ensure they are testing the correct properties, but fit for "good enough". Implements the HybridChannel type, which features a channel buffer that can withstand overflows. It does so by providing a dequeue behind a mutex. Without overflow, will push messages into the lock free ArrayQueue implemented by crossbeam_queue; when that buffer fills, will use the locking portion provided by the Mutex<VecDequeue>.
In the future we can even further optimize this, perhaps with some linked list implementations of lock-free channels, but, like the benchmarks, this fits the "good enough" bar for now.
2026-01-26 07:14:00 +00:00
|
|
|
|
2026-02-06 11:25:37 +00:00
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
|
|
|
|
use crossbeam_queue::{ArrayQueue, SegQueue};
|
feat: Stress tests, benchmarking, and non-failing queues and inboxes #3
Adds some basic benchmarking, stress tests. They still need to be properly examined to ensure they are testing the correct properties, but fit for "good enough". Implements the HybridChannel type, which features a channel buffer that can withstand overflows. It does so by providing a dequeue behind a mutex. Without overflow, will push messages into the lock free ArrayQueue implemented by crossbeam_queue; when that buffer fills, will use the locking portion provided by the Mutex<VecDequeue>.
In the future we can even further optimize this, perhaps with some linked list implementations of lock-free channels, but, like the benchmarks, this fits the "good enough" bar for now.
2026-01-26 07:14:00 +00:00
|
|
|
|
|
|
|
|
pub struct HybridChannel<T> {
|
|
|
|
|
ring: ArrayQueue<T>,
|
2026-02-06 11:25:37 +00:00
|
|
|
overflow: SegQueue<T>,
|
feat: Stress tests, benchmarking, and non-failing queues and inboxes #3
Adds some basic benchmarking, stress tests. They still need to be properly examined to ensure they are testing the correct properties, but fit for "good enough". Implements the HybridChannel type, which features a channel buffer that can withstand overflows. It does so by providing a dequeue behind a mutex. Without overflow, will push messages into the lock free ArrayQueue implemented by crossbeam_queue; when that buffer fills, will use the locking portion provided by the Mutex<VecDequeue>.
In the future we can even further optimize this, perhaps with some linked list implementations of lock-free channels, but, like the benchmarks, this fits the "good enough" bar for now.
2026-01-26 07:14:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<T> HybridChannel<T> {
|
|
|
|
|
pub fn new(capacity: usize) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
ring: ArrayQueue::new(capacity),
|
2026-02-06 11:25:37 +00:00
|
|
|
overflow: SegQueue::new(),
|
feat: Stress tests, benchmarking, and non-failing queues and inboxes #3
Adds some basic benchmarking, stress tests. They still need to be properly examined to ensure they are testing the correct properties, but fit for "good enough". Implements the HybridChannel type, which features a channel buffer that can withstand overflows. It does so by providing a dequeue behind a mutex. Without overflow, will push messages into the lock free ArrayQueue implemented by crossbeam_queue; when that buffer fills, will use the locking portion provided by the Mutex<VecDequeue>.
In the future we can even further optimize this, perhaps with some linked list implementations of lock-free channels, but, like the benchmarks, this fits the "good enough" bar for now.
2026-01-26 07:14:00 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn push(&self, value: T) -> Result<(), T> {
|
|
|
|
|
match self.ring.push(value) {
|
|
|
|
|
Ok(()) => Ok(()),
|
|
|
|
|
Err(v) => {
|
2026-02-06 11:25:37 +00:00
|
|
|
self.overflow.push(v);
|
feat: Stress tests, benchmarking, and non-failing queues and inboxes #3
Adds some basic benchmarking, stress tests. They still need to be properly examined to ensure they are testing the correct properties, but fit for "good enough". Implements the HybridChannel type, which features a channel buffer that can withstand overflows. It does so by providing a dequeue behind a mutex. Without overflow, will push messages into the lock free ArrayQueue implemented by crossbeam_queue; when that buffer fills, will use the locking portion provided by the Mutex<VecDequeue>.
In the future we can even further optimize this, perhaps with some linked list implementations of lock-free channels, but, like the benchmarks, this fits the "good enough" bar for now.
2026-01-26 07:14:00 +00:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn pop(&self) -> Option<T> {
|
|
|
|
|
if let Some(value) = self.ring.pop() {
|
|
|
|
|
return Some(value);
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-06 11:25:37 +00:00
|
|
|
match self.overflow.pop() {
|
|
|
|
|
Some(value) => {
|
|
|
|
|
Some(value)
|
|
|
|
|
}
|
|
|
|
|
None => None,
|
|
|
|
|
}
|
feat: Stress tests, benchmarking, and non-failing queues and inboxes #3
Adds some basic benchmarking, stress tests. They still need to be properly examined to ensure they are testing the correct properties, but fit for "good enough". Implements the HybridChannel type, which features a channel buffer that can withstand overflows. It does so by providing a dequeue behind a mutex. Without overflow, will push messages into the lock free ArrayQueue implemented by crossbeam_queue; when that buffer fills, will use the locking portion provided by the Mutex<VecDequeue>.
In the future we can even further optimize this, perhaps with some linked list implementations of lock-free channels, but, like the benchmarks, this fits the "good enough" bar for now.
2026-01-26 07:14:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) struct Receiver<T> {
|
|
|
|
|
queue: Arc<HybridChannel<T>>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<T> Receiver<T> {
|
|
|
|
|
pub fn new(capacity: usize) -> Self {
|
|
|
|
|
let queue = Arc::new(HybridChannel::new(capacity));
|
|
|
|
|
|
|
|
|
|
Self { queue }
|
|
|
|
|
}
|
|
|
|
|
pub fn try_recv(&self) -> Option<T> {
|
|
|
|
|
return self.queue.pop();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn new_sender(&self) -> Sender<T> {
|
|
|
|
|
Sender {
|
|
|
|
|
queue: self.queue.clone(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) struct Sender<T> {
|
|
|
|
|
queue: Arc<HybridChannel<T>>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<T> Sender<T> {
|
|
|
|
|
pub fn try_send(&self, value: T) -> Result<(), T> {
|
|
|
|
|
return self.queue.push(value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|