1#[cfg(feature = "alloc")]
2use alloc::boxed::Box;
3
4#[cfg(feature = "alloc")]
5use super::CapacityError;
6use core::mem::MaybeUninit;
7
8#[cfg(not(feature = "loom"))]
15use core::cell::UnsafeCell;
16#[cfg(not(feature = "loom"))]
17use core::sync::atomic::{AtomicUsize, Ordering};
18#[cfg(feature = "loom")]
19use loom::cell::UnsafeCell;
20#[cfg(feature = "loom")]
21use loom::sync::atomic::{AtomicUsize, Ordering};
22
23#[cfg(feature = "loom")]
24unsafe fn write_cell<T>(cell: &UnsafeCell<MaybeUninit<T>>, record: T) {
25 cell.with_mut(|ptr| unsafe { (*ptr).write(record) });
26}
27#[cfg(not(feature = "loom"))]
28unsafe fn write_cell<T>(cell: &UnsafeCell<MaybeUninit<T>>, record: T) {
29 unsafe { (*cell.get()).write(record) };
30}
31
32#[cfg(feature = "loom")]
33unsafe fn read_cell<T>(cell: &UnsafeCell<MaybeUninit<T>>) -> T {
34 cell.with(|ptr| unsafe { (*ptr).assume_init_read() })
35}
36#[cfg(not(feature = "loom"))]
37unsafe fn read_cell<T>(cell: &UnsafeCell<MaybeUninit<T>>) -> T {
38 unsafe { (*cell.get()).assume_init_read() }
39}
40
41#[cfg(all(feature = "loom", feature = "alloc"))]
42unsafe fn drop_cell<T>(cell: &UnsafeCell<MaybeUninit<T>>) {
43 cell.with_mut(|ptr| unsafe { (*ptr).assume_init_drop() });
44}
45#[cfg(all(not(feature = "loom"), feature = "alloc"))]
46unsafe fn drop_cell<T>(cell: &UnsafeCell<MaybeUninit<T>>) {
47 unsafe { (*cell.get()).assume_init_drop() };
48}
49
50struct Cell<T> {
57 sequence: AtomicUsize,
58 data: UnsafeCell<MaybeUninit<T>>,
59}
60
61#[inline]
67fn cells_push<T>(cells: &[Cell<T>], enqueue_pos: &AtomicUsize, record: T) -> Result<(), T> {
68 let mask = cells.len() - 1;
69 let mut pos = enqueue_pos.load(Ordering::Relaxed);
70 loop {
71 let cell = &cells[pos & mask];
72 let sequence = cell.sequence.load(Ordering::Acquire);
73 let diff = sequence.wrapping_sub(pos) as isize;
74 if diff == 0 {
75 match enqueue_pos.compare_exchange_weak(
76 pos,
77 pos.wrapping_add(1),
78 Ordering::Relaxed,
79 Ordering::Relaxed,
80 ) {
81 Ok(_) => {
82 unsafe {
85 write_cell(&cell.data, record);
86 }
87 cell.sequence.store(pos.wrapping_add(1), Ordering::Release);
88 return Ok(());
89 }
90 Err(actual) => pos = actual,
91 }
92 } else if diff < 0 {
93 return Err(record);
94 } else {
95 pos = enqueue_pos.load(Ordering::Relaxed);
96 }
97 }
98}
99
100#[inline]
101fn cells_dequeue<T>(cells: &[Cell<T>], dequeue_pos: &AtomicUsize) -> Option<T> {
102 let mask = cells.len() - 1;
103 let cap = cells.len();
104 let mut pos = dequeue_pos.load(Ordering::Relaxed);
105 loop {
106 let cell = &cells[pos & mask];
107 let sequence = cell.sequence.load(Ordering::Acquire);
108 let diff = sequence.wrapping_sub(pos.wrapping_add(1)) as isize;
109 if diff == 0 {
110 match dequeue_pos.compare_exchange_weak(
111 pos,
112 pos.wrapping_add(1),
113 Ordering::Relaxed,
114 Ordering::Relaxed,
115 ) {
116 Ok(_) => {
117 let record = unsafe { read_cell(&cell.data) };
121 cell.sequence
122 .store(pos.wrapping_add(cap), Ordering::Release);
123 return Some(record);
124 }
125 Err(actual) => pos = actual,
126 }
127 } else if diff < 0 {
128 return None;
129 } else {
130 pos = dequeue_pos.load(Ordering::Relaxed);
131 }
132 }
133}
134
135#[cfg(feature = "alloc")]
148pub struct Ring<T> {
149 buf: Box<[Cell<T>]>,
150 enqueue_pos: AtomicUsize,
152 dequeue_pos: AtomicUsize,
153 mask: usize,
154 cap: usize,
155}
156
157#[cfg(feature = "alloc")]
160unsafe impl<T: Send> Send for Ring<T> {}
161
162#[cfg(feature = "alloc")]
170unsafe impl<T: Send> Sync for Ring<T> {}
171
172#[cfg(feature = "alloc")]
173impl<T> Ring<T> {
174 pub fn new(cap: usize) -> Result<Self, CapacityError> {
178 if cap == 0 {
179 return Err(CapacityError);
180 }
181 Ok(Self::build(cap))
182 }
183
184 #[must_use]
190 pub fn with_capacity(cap: usize) -> Self {
191 Self::build(cap.max(1))
192 }
193
194 fn build(cap: usize) -> Self {
197 let cap = cap.next_power_of_two().max(2);
198 let buf = (0..cap)
199 .map(|index| Cell {
200 sequence: AtomicUsize::new(index),
201 data: UnsafeCell::new(MaybeUninit::uninit()),
202 })
203 .collect::<Box<[_]>>();
204
205 Self {
206 buf,
207 enqueue_pos: AtomicUsize::new(0),
208 dequeue_pos: AtomicUsize::new(0),
209 mask: cap - 1,
210 cap,
211 }
212 }
213
214 pub fn push(&self, record: T) -> Result<(), T> {
220 cells_push(&self.buf, &self.enqueue_pos, record)
221 }
222
223 pub fn dequeue(&self) -> Option<T> {
231 cells_dequeue(&self.buf, &self.dequeue_pos)
232 }
233
234 pub fn len(&self) -> usize {
236 let enqueue = self.enqueue_pos.load(Ordering::Acquire);
237 let dequeue = self.dequeue_pos.load(Ordering::Acquire);
238 enqueue.wrapping_sub(dequeue)
239 }
240
241 pub fn is_empty(&self) -> bool {
243 self.len() == 0
244 }
245
246 pub fn cap(&self) -> usize {
248 self.cap
249 }
250}
251
252#[cfg(feature = "alloc")]
253impl<T> Drop for Ring<T> {
254 fn drop(&mut self) {
255 let dequeue = self.dequeue_pos.load(Ordering::Relaxed);
258 let enqueue = self.enqueue_pos.load(Ordering::Relaxed);
259
260 let mut pos = dequeue;
261 while pos != enqueue {
262 let slot = pos & self.mask;
263 let published = self.buf[slot].sequence.load(Ordering::Relaxed) == pos.wrapping_add(1);
264 if published {
265 unsafe {
268 drop_cell(&self.buf[slot].data);
269 }
270 }
271 pos = pos.wrapping_add(1);
272 }
273 }
274}
275
276#[cfg(feature = "alloc")]
281const PREFETCH_DISTANCE: usize = 16;
282
283#[cfg(feature = "alloc")]
288pub struct Drainer<'ring, T> {
289 ring: &'ring Ring<T>,
290}
291
292#[cfg(feature = "alloc")]
293impl<'ring, T> Drainer<'ring, T> {
294 pub fn new(ring: &'ring Ring<T>) -> Self {
296 Self { ring }
297 }
298
299 pub fn drain_into(&mut self, out: &mut [T]) -> usize {
306 let mut count = 0;
307 while count < out.len() {
308 match self.ring.dequeue() {
309 Some(record) => {
310 let ahead = count + PREFETCH_DISTANCE;
314 if ahead < out.len() {
315 let slot = unsafe { out.as_ptr().add(ahead) };
318 crate::arch::prefetch_for_write(slot.cast());
319 }
320 out[count] = record;
321 count += 1;
322 }
323 None => break,
324 }
325 }
326 count
327 }
328}
329
330pub struct StaticRing<T, const N: usize> {
345 buf: [Cell<T>; N],
346 enqueue_pos: AtomicUsize,
347 dequeue_pos: AtomicUsize,
348}
349
350unsafe impl<T: Send, const N: usize> Send for StaticRing<T, N> {}
353unsafe impl<T: Send, const N: usize> Sync for StaticRing<T, N> {}
354
355impl<T, const N: usize> StaticRing<T, N> {
356 #[must_use]
365 #[cfg(not(feature = "loom"))]
366 pub const fn new() -> Self {
367 assert!(
368 N >= 2 && N.is_power_of_two(),
369 "StaticRing capacity N must be a power of two >= 2"
370 );
371 let mut buf = [const {
372 Cell {
373 sequence: AtomicUsize::new(0),
374 data: UnsafeCell::new(MaybeUninit::uninit()),
375 }
376 }; N];
377 let mut index = 0;
378 while index < N {
379 buf[index].sequence = AtomicUsize::new(index);
380 index += 1;
381 }
382 Self {
383 buf,
384 enqueue_pos: AtomicUsize::new(0),
385 dequeue_pos: AtomicUsize::new(0),
386 }
387 }
388
389 #[must_use]
390 #[cfg(feature = "loom")]
391 pub fn new() -> Self {
392 assert!(
393 N >= 2 && N.is_power_of_two(),
394 "StaticRing capacity N must be a power of two >= 2"
395 );
396 Self {
397 buf: core::array::from_fn(|index| Cell {
398 sequence: AtomicUsize::new(index),
399 data: UnsafeCell::new(MaybeUninit::uninit()),
400 }),
401 enqueue_pos: AtomicUsize::new(0),
402 dequeue_pos: AtomicUsize::new(0),
403 }
404 }
405
406 pub fn push(&self, record: T) -> Result<(), T> {
408 cells_push(&self.buf, &self.enqueue_pos, record)
409 }
410
411 pub fn dequeue(&self) -> Option<T> {
413 cells_dequeue(&self.buf, &self.dequeue_pos)
414 }
415
416 #[must_use]
418 pub fn len(&self) -> usize {
419 self.enqueue_pos
420 .load(Ordering::Acquire)
421 .wrapping_sub(self.dequeue_pos.load(Ordering::Acquire))
422 }
423
424 #[must_use]
425 pub fn is_empty(&self) -> bool {
426 self.len() == 0
427 }
428
429 #[must_use]
430 pub const fn capacity(&self) -> usize {
431 N
432 }
433}
434
435impl<T, const N: usize> Default for StaticRing<T, N> {
436 fn default() -> Self {
437 Self::new()
438 }
439}
440
441impl<T, const N: usize> Drop for StaticRing<T, N> {
442 fn drop(&mut self) {
443 while self.dequeue().is_some() {}
446 }
447}
448
449#[cfg(all(test, not(feature = "loom")))]
454#[allow(clippy::unwrap_used, clippy::expect_used)]
455mod tests {
456 extern crate std;
457
458 use std::sync::Arc;
459 use std::sync::atomic::{AtomicUsize, Ordering};
460 use std::thread;
461
462 use super::StaticRing;
463
464 #[cfg(feature = "alloc")]
468 mod ring_tests {
469 use super::*;
470 use std::sync::Arc;
471 use std::thread;
472
473 use super::super::{Drainer, Ring};
474 use alloc::collections::VecDeque;
475 use alloc::vec;
476 use alloc::vec::Vec;
477
478 #[proxima::test]
479 #[case::push_all_drain_all(8)]
480 #[case::small_cap(2)]
481 #[case::large_cap(64)]
482 async fn happy_push_n_drain_n_in_order(#[case] cap: usize) {
483 let ring = Ring::new(cap).unwrap();
484 for item in 0..cap {
485 ring.push(item as u32).unwrap();
486 }
487
488 let mut out = vec![0u32; cap];
489 let mut drainer = Drainer::new(&ring);
490 let received = drainer.drain_into(&mut out);
491
492 assert_eq!(received, cap);
493 for (idx, value) in out.iter().enumerate() {
494 assert_eq!(*value, idx as u32);
495 }
496 }
497
498 #[proxima::test]
499 #[case::small(2)]
500 #[case::many(16)]
501 async fn sad_push_into_full_ring_returns_full(#[case] cap: usize) {
502 let ring = Ring::<u64>::new(cap).unwrap();
503 for item in 0..ring.cap() {
504 ring.push(item as u64).unwrap();
505 }
506
507 assert_eq!(ring.push(99), Err(99));
509 }
510
511 #[test]
516 fn worked_example_fifo_full_at_cap_then_reclaim() {
517 let ring = Ring::new(4).unwrap();
518 assert_eq!(ring.cap(), 4);
519
520 for value in 0..4u32 {
521 ring.push(value).unwrap();
522 }
523 assert_eq!(ring.push(99), Err(99));
524
525 let mut out = [0u32; 4];
526 let mut drainer = Drainer::new(&ring);
527 assert_eq!(drainer.drain_into(&mut out), 4);
528 assert_eq!(out, [0, 1, 2, 3]);
529
530 ring.push(7).unwrap();
532 let mut out2 = [0u32; 1];
533 assert_eq!(drainer.drain_into(&mut out2), 1);
534 assert_eq!(out2[0], 7);
535 }
536
537 #[test]
538 fn edge_min_cap_push_full_drain_push_again() {
539 let ring = Ring::new(2).unwrap();
540
541 ring.push(42u32).unwrap();
542 ring.push(43u32).unwrap();
543 assert_eq!(ring.push(99u32), Err(99));
544
545 let mut out = [0u32; 2];
546 let mut drainer = Drainer::new(&ring);
547 assert_eq!(drainer.drain_into(&mut out), 2);
548 assert_eq!(out, [42, 43]);
549
550 ring.push(7u32).unwrap();
551 let mut out2 = [0u32; 1];
552 assert_eq!(drainer.drain_into(&mut out2), 1);
553 assert_eq!(out2[0], 7);
554 }
555
556 #[test]
557 fn cap_rounds_up_to_power_of_two_min_two() {
558 assert_eq!(Ring::<u8>::new(1).unwrap().cap(), 2);
559 assert_eq!(Ring::<u8>::new(3).unwrap().cap(), 4);
560 assert_eq!(Ring::<u8>::new(1000).unwrap().cap(), 1024);
561 assert_eq!(Ring::<u8>::new(4096).unwrap().cap(), 4096);
562 }
563
564 #[test]
565 fn with_capacity_is_infallible_and_clamps_zero_to_min() {
566 assert_eq!(Ring::<u8>::with_capacity(0).cap(), 2);
569 assert_eq!(Ring::<u8>::with_capacity(1).cap(), 2);
570 assert_eq!(Ring::<u8>::with_capacity(3).cap(), 4);
571 let ring = Ring::with_capacity(1000);
572 assert_eq!(ring.cap(), 1024);
573 ring.push(7u32).unwrap();
574 assert_eq!(ring.dequeue(), Some(7));
575 }
576
577 #[test]
578 fn drop_items_inside_ring_are_dropped_properly() {
579 let counter = Arc::new(AtomicUsize::new(0));
580
581 struct Counted(Arc<AtomicUsize>);
582 impl Drop for Counted {
583 fn drop(&mut self) {
584 self.0.fetch_add(1, Ordering::Relaxed);
585 }
586 }
587
588 {
589 let ring = Ring::new(4).unwrap();
590 assert!(ring.push(Counted(Arc::clone(&counter))).is_ok());
592 assert!(ring.push(Counted(Arc::clone(&counter))).is_ok());
593 }
595
596 assert_eq!(counter.load(Ordering::Relaxed), 2);
597 }
598
599 #[test]
600 fn concurrency_single_producer_order_preserved() {
601 const ITEMS: u32 = 10_000;
602 let ring = Arc::new(Ring::<u32>::new(512).unwrap());
603
604 let producer_ring = Arc::clone(&ring);
605 let producer = thread::spawn(move || {
606 let mut pushed = 0u32;
607 while pushed < ITEMS {
608 if producer_ring.push(pushed).is_ok() {
609 pushed += 1;
610 } else {
611 thread::yield_now();
612 }
613 }
614 });
615
616 let consumer_ring = Arc::clone(&ring);
617 let consumer = thread::spawn(move || {
618 let mut received = Vec::with_capacity(ITEMS as usize);
619 let mut buf = vec![0u32; 64];
620 let mut drainer = Drainer::new(&*consumer_ring);
621 while received.len() < ITEMS as usize {
622 let count = drainer.drain_into(&mut buf);
623 received.extend_from_slice(&buf[..count]);
624 if count == 0 {
625 thread::yield_now();
626 }
627 }
628 received
629 });
630
631 producer.join().expect("producer panicked");
632 let received = consumer.join().expect("consumer panicked");
633
634 assert_eq!(received.len(), ITEMS as usize);
635 for (idx, value) in received.iter().enumerate() {
636 assert_eq!(*value, idx as u32);
637 }
638 }
639
640 #[test]
648 fn mpsc_many_producers_no_loss_no_tear() {
649 #[derive(Clone, Copy)]
650 struct Tagged {
651 id: u64,
652 check: u64,
653 }
654 const MAGIC: u64 = 0x9E37_79B9_7F4A_7C15;
655 fn make(id: u64) -> Tagged {
656 Tagged {
657 id,
658 check: id ^ MAGIC,
659 }
660 }
661
662 const PRODUCERS: u64 = 8;
663 const PER_PRODUCER: u64 = 4_000;
664 const TOTAL: usize = (PRODUCERS * PER_PRODUCER) as usize;
665
666 for _round in 0..5 {
667 let ring = Arc::new(Ring::<Tagged>::new(16).unwrap());
669
670 let producers: Vec<_> = (0..PRODUCERS)
671 .map(|producer_id| {
672 let ring = Arc::clone(&ring);
673 thread::spawn(move || {
674 let base = producer_id * PER_PRODUCER;
675 let mut sent = 0u64;
676 while sent < PER_PRODUCER {
677 if ring.push(make(base + sent)).is_ok() {
678 sent += 1;
679 } else {
680 thread::yield_now();
681 }
682 }
683 })
684 })
685 .collect();
686
687 let consumer_ring = Arc::clone(&ring);
688 let consumer = thread::spawn(move || {
689 let mut seen: Vec<bool> = vec![false; TOTAL];
690 let mut received = 0usize;
691 let mut buf = vec![make(0); 32];
692 let mut drainer = Drainer::new(&*consumer_ring);
693 while received < TOTAL {
694 let count = drainer.drain_into(&mut buf);
695 if count == 0 {
696 thread::yield_now();
697 continue;
698 }
699 for tagged in buf.iter().take(count) {
700 assert_eq!(tagged.check, tagged.id ^ MAGIC, "torn record");
701 let id = tagged.id as usize;
702 assert!(!seen[id], "duplicate id {id}");
703 seen[id] = true;
704 received += 1;
705 }
706 }
707 seen
708 });
709
710 for producer in producers {
711 producer.join().expect("producer panicked");
712 }
713 let seen = consumer.join().expect("consumer panicked");
714 assert!(seen.into_iter().all(|hit| hit), "missing ids");
715 }
716 }
717
718 #[test]
724 fn worked_example_mpmc_two_consumers_fifo_then_reclaim() {
725 let ring = Ring::new(2).unwrap();
726 ring.push(b'A').unwrap();
727 ring.push(b'B').unwrap();
728 assert_eq!(
729 ring.push(b'C'),
730 Err(b'C'),
731 "full ring hands the record back"
732 );
733
734 let first = ring.dequeue().expect("first pop");
736 let second = ring.dequeue().expect("second pop");
737 assert_eq!([first, second], [b'A', b'B'], "FIFO order across consumers");
738 assert_eq!(ring.dequeue(), None, "empty after both consumed");
739
740 ring.push(b'D').unwrap();
742 assert_eq!(ring.dequeue(), Some(b'D'));
743 }
744
745 #[test]
754 fn mpmc_many_producers_many_consumers_no_loss_no_tear() {
755 #[derive(Clone, Copy)]
756 struct Tagged {
757 id: u64,
758 check: u64,
759 }
760 const MAGIC: u64 = 0x9E37_79B9_7F4A_7C15;
761 fn make(id: u64) -> Tagged {
762 Tagged {
763 id,
764 check: id ^ MAGIC,
765 }
766 }
767
768 const PRODUCERS: u64 = 8;
769 const CONSUMERS: usize = 4;
770 const PER_PRODUCER: u64 = 4_000;
771 const TOTAL: usize = (PRODUCERS * PER_PRODUCER) as usize;
772
773 for _round in 0..5 {
774 let ring = Arc::new(Ring::<Tagged>::new(16).unwrap());
776 let seen: Arc<Vec<AtomicUsize>> =
780 Arc::new((0..TOTAL).map(|_| AtomicUsize::new(0)).collect());
781 let received = Arc::new(AtomicUsize::new(0));
782
783 let producers: Vec<_> = (0..PRODUCERS)
784 .map(|producer_id| {
785 let ring = Arc::clone(&ring);
786 thread::spawn(move || {
787 let base = producer_id * PER_PRODUCER;
788 let mut sent = 0u64;
789 while sent < PER_PRODUCER {
790 if ring.push(make(base + sent)).is_ok() {
791 sent += 1;
792 } else {
793 thread::yield_now();
794 }
795 }
796 })
797 })
798 .collect();
799
800 let consumers: Vec<_> = (0..CONSUMERS)
801 .map(|_| {
802 let ring = Arc::clone(&ring);
803 let seen = Arc::clone(&seen);
804 let received = Arc::clone(&received);
805 thread::spawn(move || {
806 while received.load(Ordering::Relaxed) < TOTAL {
807 match ring.dequeue() {
808 Some(tagged) => {
809 assert_eq!(tagged.check, tagged.id ^ MAGIC, "torn record");
810 let prior = seen[tagged.id as usize]
811 .fetch_add(1, Ordering::Relaxed);
812 assert_eq!(prior, 0, "duplicate id {}", tagged.id);
813 received.fetch_add(1, Ordering::Relaxed);
814 }
815 None => thread::yield_now(),
816 }
817 }
818 })
819 })
820 .collect();
821
822 for producer in producers {
823 producer.join().expect("producer panicked");
824 }
825 for consumer in consumers {
826 consumer.join().expect("consumer panicked");
827 }
828 assert!(
829 seen.iter().all(|hit| hit.load(Ordering::Relaxed) == 1),
830 "every id consumed exactly once"
831 );
832 }
833 }
834
835 #[proxima::test]
836 #[case::tiny(4, 100)]
837 #[case::medium(32, 1_000)]
838 #[case::large(256, 10_000)]
839 async fn property_random_push_drain_interleaving_preserves_fifo(
840 #[case] cap: usize,
841 #[case] total_ops: usize,
842 ) {
843 let ring = Ring::new(cap).unwrap();
844 let mut drainer = Drainer::new(&ring);
845 let mut expected_queue: VecDeque<u32> = VecDeque::new();
846 let mut next_push_value = 0u32;
847 let mut drain_buf = vec![0u32; cap];
848
849 for op_idx in 0..total_ops {
850 let do_push = op_idx % 3 != 2;
851
852 if do_push && expected_queue.len() < cap {
853 if ring.push(next_push_value).is_ok() {
854 expected_queue.push_back(next_push_value);
855 next_push_value = next_push_value.wrapping_add(1);
856 }
857 } else {
858 let count = drainer.drain_into(&mut drain_buf);
859 for drained in drain_buf.iter().take(count) {
860 let expected = expected_queue.pop_front().expect("queue underflow");
861 assert_eq!(*drained, expected);
862 }
863 }
864 }
865 }
866
867 #[test]
868 fn invalid_cap_zero_returns_error() {
869 let result = Ring::<u32>::new(0);
870 assert!(matches!(result, Err(crate::ring::CapacityError)));
871 }
872 }
873
874 #[test]
879 fn static_ring_backs_a_static_with_no_lazy_init() {
880 static RING: StaticRing<u32, 4> = StaticRing::new();
881 assert_eq!(RING.capacity(), 4);
882 for value in [11, 22, 33, 44] {
883 RING.push(value).unwrap();
884 }
885 assert_eq!(
886 RING.push(55),
887 Err(55),
888 "a const-built ring bounds like any other"
889 );
890 assert_eq!(RING.dequeue(), Some(11));
891 }
892
893 #[test]
894 fn static_ring_fifo_roundtrip() {
895 let ring = StaticRing::<u32, 4>::new();
896 assert_eq!(ring.capacity(), 4);
897 assert!(ring.is_empty());
898 for value in [10, 20, 30] {
899 ring.push(value).unwrap();
900 }
901 assert_eq!(ring.len(), 3);
902 assert_eq!(ring.dequeue(), Some(10));
903 assert_eq!(ring.dequeue(), Some(20));
904 assert_eq!(ring.dequeue(), Some(30));
905 assert_eq!(ring.dequeue(), None);
906 }
907
908 #[test]
909 fn static_ring_full_hands_record_back_without_blocking() {
910 let ring = StaticRing::<u32, 4>::new();
911 for value in [1, 2, 3, 4] {
912 ring.push(value).unwrap();
913 }
914 assert_eq!(ring.push(5), Err(5));
916 assert_eq!(ring.len(), 4);
917 }
918
919 #[test]
920 #[should_panic(expected = "power of two")]
921 fn static_ring_non_power_of_two_panics() {
922 let _ring = StaticRing::<u32, 6>::new();
923 }
924
925 #[test]
926 fn static_ring_drop_drains_buffered_records() {
927 struct Counted(Arc<AtomicUsize>);
928 impl Drop for Counted {
929 fn drop(&mut self) {
930 self.0.fetch_add(1, Ordering::Relaxed);
931 }
932 }
933
934 let drops = Arc::new(AtomicUsize::new(0));
935 {
936 let ring = StaticRing::<Counted, 8>::new();
937 for _ in 0..3 {
938 assert!(ring.push(Counted(Arc::clone(&drops))).is_ok());
939 }
940 } assert_eq!(drops.load(Ordering::Relaxed), 3);
942 }
943
944 #[test]
945 fn static_ring_concurrent_mpmc_no_loss_no_dup() {
946 const PRODUCERS: usize = 4;
947 const PER: u32 = 10_000;
948 let total = PRODUCERS * PER as usize;
949
950 let ring = Arc::new(StaticRing::<u32, 1024>::new());
951 let seen = Arc::new(AtomicUsize::new(0));
952 let sum = Arc::new(AtomicUsize::new(0));
953 let mut handles = std::vec::Vec::new();
954
955 for producer in 0..PRODUCERS {
956 let ring = Arc::clone(&ring);
957 handles.push(thread::spawn(move || {
958 for index in 0..PER {
959 let value = producer as u32 * PER + index;
960 while ring.push(value).is_err() {
961 std::hint::spin_loop();
962 }
963 }
964 }));
965 }
966 for _ in 0..2 {
967 let ring = Arc::clone(&ring);
968 let seen = Arc::clone(&seen);
969 let sum = Arc::clone(&sum);
970 handles.push(thread::spawn(move || {
971 loop {
972 if let Some(value) = ring.dequeue() {
973 seen.fetch_add(1, Ordering::Relaxed);
974 sum.fetch_add(value as usize, Ordering::Relaxed);
975 } else if seen.load(Ordering::Relaxed) >= total {
976 break;
977 } else {
978 std::hint::spin_loop();
979 }
980 }
981 }));
982 }
983 for handle in handles {
984 handle.join().unwrap();
985 }
986
987 assert_eq!(seen.load(Ordering::Relaxed), total, "no item lost");
988 let expected: usize = (0..total).sum();
990 assert_eq!(
991 sum.load(Ordering::Relaxed),
992 expected,
993 "no duplicate or torn read"
994 );
995 }
996}