1#![deny(missing_docs)]
47#![allow(unsafe_code)]
48
49use std::cell::{Cell, UnsafeCell};
50use std::marker::PhantomData;
51use std::mem::MaybeUninit;
52use std::sync::Arc;
53use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
54
55const CACHE_LINE: usize = 64;
56
57#[repr(C)]
58struct PaddedAtomicUsize {
59 value: AtomicUsize,
60 _pad: [u8; CACHE_LINE - size_of::<AtomicUsize>()],
61}
62
63impl PaddedAtomicUsize {
64 const fn new(v: usize) -> Self {
65 Self {
66 value: AtomicUsize::new(v),
67 _pad: [0; CACHE_LINE - size_of::<AtomicUsize>()],
68 }
69 }
70}
71
72#[repr(align(32))]
75struct Slot<T> {
76 sequence: AtomicUsize,
77 value: UnsafeCell<MaybeUninit<T>>,
78}
79
80struct RingBuffer<T> {
81 slots: Box<[Slot<T>]>,
82 mask: usize,
83 closed: AtomicBool, head: PaddedAtomicUsize,
85 tail: PaddedAtomicUsize,
86}
87
88unsafe impl<T: Send> Send for RingBuffer<T> {}
91unsafe impl<T: Send> Sync for RingBuffer<T> {}
92
93impl<T> Drop for RingBuffer<T> {
94 fn drop(&mut self) {
95 let mut head = self.head.value.load(Ordering::Relaxed);
98 let tail = self.tail.value.load(Ordering::Relaxed);
99 while head != tail {
100 let slot = &self.slots[head & self.mask];
101 unsafe { (*slot.value.get()).assume_init_drop() };
104 head = head.wrapping_add(1);
105 }
106 }
107}
108
109#[derive(Debug, Clone, Copy)]
111pub enum WaitStrategy {
112 SpinLoop,
114 Yield,
116 Sleep(std::time::Duration),
118}
119
120impl WaitStrategy {
121 #[inline]
122 fn wait(&self) {
123 match self {
124 WaitStrategy::SpinLoop => std::hint::spin_loop(),
125 WaitStrategy::Yield => std::thread::yield_now(),
126 WaitStrategy::Sleep(d) => std::thread::sleep(*d),
127 }
128 }
129}
130
131pub struct Producer<T> {
133 inner: Arc<RingBuffer<T>>,
134 _not_sync: PhantomData<Cell<()>>,
135}
136
137pub struct Consumer<T> {
139 inner: Arc<RingBuffer<T>>,
140 _not_sync: PhantomData<Cell<()>>,
141}
142
143#[derive(Debug, Clone, Copy, PartialEq, Eq)]
145pub struct RecvError;
146
147impl std::fmt::Display for RecvError {
148 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149 write!(f, "producer disconnected")
150 }
151}
152
153impl std::error::Error for RecvError {}
154
155#[derive(Debug, PartialEq, Eq)]
157pub struct SendError<T>(pub T);
158
159impl<T: std::fmt::Debug> std::fmt::Display for SendError<T> {
160 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161 write!(f, "consumer disconnected; value: {:?}", self.0)
162 }
163}
164
165impl<T: std::fmt::Debug + 'static> std::error::Error for SendError<T> {}
166
167#[derive(Debug, Clone, Copy, PartialEq, Eq)]
169pub enum TryRecvError {
170 Empty,
172 Disconnected,
174}
175
176impl std::fmt::Display for TryRecvError {
177 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
178 match self {
179 TryRecvError::Empty => write!(f, "buffer empty"),
180 TryRecvError::Disconnected => write!(f, "producer disconnected"),
181 }
182 }
183}
184
185impl std::error::Error for TryRecvError {}
186
187#[derive(Debug, PartialEq, Eq)]
189pub enum TrySendError<T> {
190 Full(T),
192 Disconnected(T),
194}
195
196impl<T: std::fmt::Debug> std::fmt::Display for TrySendError<T> {
197 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
198 match self {
199 TrySendError::Full(v) => write!(f, "buffer full; value: {:?}", v),
200 TrySendError::Disconnected(v) => write!(f, "consumer disconnected; value: {:?}", v),
201 }
202 }
203}
204
205impl<T: std::fmt::Debug + 'static> std::error::Error for TrySendError<T> {}
206
207#[derive(Debug, Clone, Copy, PartialEq, Eq)]
209pub struct InvalidCapacity(pub usize);
210
211impl std::fmt::Display for InvalidCapacity {
212 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
213 write!(f, "capacity {} is not a non-zero power of two", self.0)
214 }
215}
216
217impl std::error::Error for InvalidCapacity {}
218
219impl<T> Producer<T> {
220 #[must_use]
222 pub fn is_disconnected(&self) -> bool {
223 self.inner.closed.load(Ordering::Acquire)
224 }
225
226 #[inline]
233 pub fn try_push(&self, value: T) -> Result<(), TrySendError<T>> {
234 if self.inner.closed.load(Ordering::Acquire) {
235 return Err(TrySendError::Disconnected(value));
236 }
237 let rb = &*self.inner;
238 let tail = rb.tail.value.load(Ordering::Relaxed);
239 let slot = &rb.slots[tail & rb.mask];
240 let seq = slot.sequence.load(Ordering::Acquire);
241
242 if seq != tail {
243 return Err(TrySendError::Full(value));
244 }
245
246 unsafe { (*slot.value.get()).write(value) };
248 slot.sequence.store(tail + 1, Ordering::Release);
249 rb.tail.value.store(tail + 1, Ordering::Relaxed);
250 Ok(())
251 }
252
253 #[must_use]
285 pub fn len(&self) -> usize {
286 let rb = &*self.inner;
287 let tail = rb.tail.value.load(Ordering::Relaxed);
288 let head = rb.head.value.load(Ordering::Relaxed);
289 tail.wrapping_sub(head)
290 }
291
292 #[must_use]
294 pub fn is_empty(&self) -> bool {
295 self.len() == 0
296 }
297
298 #[must_use]
300 pub fn is_full(&self) -> bool {
301 self.len() == self.capacity()
302 }
303
304 #[must_use]
306 pub fn capacity(&self) -> usize {
307 self.inner.mask + 1
308 }
309
310 pub fn push(&self, value: T, strategy: &WaitStrategy) -> Result<(), SendError<T>> {
316 let mut v = value;
317 loop {
318 match self.try_push(v) {
319 Ok(()) => return Ok(()),
320 Err(TrySendError::Disconnected(returned)) => return Err(SendError(returned)),
321 Err(TrySendError::Full(returned)) => {
322 strategy.wait();
323 v = returned;
324 }
325 }
326 }
327 }
328}
329
330impl<T> Drop for Producer<T> {
331 fn drop(&mut self) {
332 self.inner.closed.store(true, Ordering::Release);
333 }
334}
335
336impl<T: Copy> Producer<T> {
337 #[inline]
343 pub fn push_slice(&self, src: &[T]) -> usize {
344 let mut count = 0;
345 for &item in src {
346 match self.try_push(item) {
347 Ok(()) => count += 1,
348 Err(_) => break,
349 }
350 }
351 count
352 }
353}
354
355impl<T> Drop for Consumer<T> {
356 fn drop(&mut self) {
357 self.inner.closed.store(true, Ordering::Release);
358 }
359}
360
361impl<T> Consumer<T> {
362 #[must_use]
364 pub fn is_disconnected(&self) -> bool {
365 self.inner.closed.load(Ordering::Acquire)
366 }
367
368 #[inline]
375 pub fn try_pop(&self) -> Result<T, TryRecvError> {
376 let rb = &*self.inner;
377 let head = rb.head.value.load(Ordering::Relaxed);
378 let slot = &rb.slots[head & rb.mask];
379 let seq = slot.sequence.load(Ordering::Acquire);
380
381 if seq != head + 1 {
382 if rb.closed.load(Ordering::Acquire) {
383 if slot.sequence.load(Ordering::Acquire) == head + 1 {
387 let value = unsafe { (*slot.value.get()).assume_init_read() };
388 slot.sequence.store(head + rb.mask + 1, Ordering::Release);
389 rb.head.value.store(head + 1, Ordering::Relaxed);
390 return Ok(value);
391 }
392 return Err(TryRecvError::Disconnected);
393 }
394 return Err(TryRecvError::Empty);
395 }
396
397 let value = unsafe { (*slot.value.get()).assume_init_read() };
401 slot.sequence.store(head + rb.mask + 1, Ordering::Release);
402 rb.head.value.store(head + 1, Ordering::Relaxed);
403 Ok(value)
404 }
405
406 #[must_use]
439 pub fn len(&self) -> usize {
440 let rb = &*self.inner;
441 let tail = rb.tail.value.load(Ordering::Relaxed);
442 let head = rb.head.value.load(Ordering::Relaxed);
443 tail.wrapping_sub(head)
444 }
445
446 #[must_use]
448 pub fn is_empty(&self) -> bool {
449 self.len() == 0
450 }
451
452 #[must_use]
454 pub fn capacity(&self) -> usize {
455 self.inner.mask + 1
456 }
457
458 pub fn pop(&self, strategy: &WaitStrategy) -> Result<T, RecvError> {
464 loop {
465 match self.try_pop() {
466 Ok(v) => return Ok(v),
467 Err(TryRecvError::Disconnected) => return Err(RecvError),
468 Err(TryRecvError::Empty) => strategy.wait(),
469 }
470 }
471 }
472}
473
474impl<T: Copy> Consumer<T> {
475 #[inline]
481 pub fn pop_into_slice(&self, dst: &mut [T]) -> usize {
482 let mut count = 0;
483 for slot in dst.iter_mut() {
484 match self.try_pop() {
485 Ok(v) => {
486 *slot = v;
487 count += 1;
488 }
489 Err(_) => break,
490 }
491 }
492 count
493 }
494}
495
496pub fn ring<T: Send>(capacity: usize) -> Result<(Producer<T>, Consumer<T>), InvalidCapacity> {
504 if capacity == 0 || !capacity.is_power_of_two() {
505 return Err(InvalidCapacity(capacity));
506 }
507
508 let slots: Vec<Slot<T>> = (0..capacity)
509 .map(|i| Slot {
510 sequence: AtomicUsize::new(i),
511 value: UnsafeCell::new(MaybeUninit::uninit()),
512 })
513 .collect();
514
515 let inner = Arc::new(RingBuffer {
516 slots: slots.into_boxed_slice(),
517 mask: capacity - 1,
518 head: PaddedAtomicUsize::new(0),
519 tail: PaddedAtomicUsize::new(0),
520 closed: AtomicBool::new(false),
521 });
522
523 Ok((
524 Producer {
525 inner: Arc::clone(&inner),
526 _not_sync: PhantomData,
527 },
528 Consumer {
529 inner,
530 _not_sync: PhantomData,
531 },
532 ))
533}
534
535#[cfg(test)]
536mod tests {
537 use super::*;
538 use std::thread;
539
540 #[test]
541 fn push_pop_single() {
542 let (tx, rx) = ring(4).unwrap();
543 assert!(tx.try_push(42).is_ok());
544 assert_eq!(rx.try_pop(), Ok(42));
545 }
546
547 #[test]
548 fn full_returns_err() {
549 let (tx, _rx) = ring(2).unwrap();
550 assert!(tx.try_push(1).is_ok());
551 assert!(tx.try_push(2).is_ok());
552 assert_eq!(tx.try_push(3), Err(TrySendError::Full(3)));
553 }
554
555 #[test]
556 fn empty_returns_none() {
557 let (_tx, rx) = ring::<i32>(4).unwrap();
558 assert_eq!(rx.try_pop(), Err(TryRecvError::Empty));
559 }
560
561 #[test]
562 fn fifo_order() {
563 let (tx, rx) = ring(4).unwrap();
564 for i in 0..4 {
565 tx.try_push(i).unwrap();
566 }
567 for i in 0..4 {
568 assert_eq!(rx.try_pop(), Ok(i));
569 }
570 }
571
572 #[test]
573 fn wrap_around() {
574 let (tx, rx) = ring(4).unwrap();
575 for round in 0..3 {
576 for i in 0..4 {
577 tx.try_push(round * 4 + i).unwrap();
578 }
579 for i in 0..4 {
580 assert_eq!(rx.try_pop(), Ok(round * 4 + i));
581 }
582 }
583 }
584
585 #[test]
586 fn concurrent_spsc() {
587 let (tx, rx) = ring(64).unwrap();
588 let count = 100_000;
589
590 let producer = thread::spawn(move || {
591 for i in 0..count {
592 loop {
593 match tx.try_push(i) {
594 Ok(()) => break,
595 Err(TrySendError::Full(_)) => std::hint::spin_loop(),
596 Err(TrySendError::Disconnected(_)) => return,
597 }
598 }
599 }
600 });
601
602 let consumer = thread::spawn(move || {
603 let mut received = Vec::with_capacity(count);
604 while received.len() < count {
605 match rx.try_pop() {
606 Ok(v) => received.push(v),
607 Err(TryRecvError::Empty) => std::hint::spin_loop(),
608 Err(TryRecvError::Disconnected) => break,
609 }
610 }
611 received
612 });
613
614 producer.join().unwrap();
615 let received = consumer.join().unwrap();
616 let expected: Vec<usize> = (0..count).collect();
617 assert_eq!(received, expected);
618 }
619
620 #[test]
621 fn len_and_capacity() {
622 let (tx, rx) = ring(4).unwrap();
623 assert_eq!(tx.capacity(), 4);
624 assert!(rx.is_empty());
625 tx.try_push(1).unwrap();
626 assert_eq!(rx.len(), 1);
627 tx.try_push(2).unwrap();
628 tx.try_push(3).unwrap();
629 tx.try_push(4).unwrap();
630 assert!(tx.is_full());
631 }
632
633 #[test]
634 fn push_slice_all_fit() {
635 let (tx, rx) = ring(8).unwrap();
636 let data = [1u32, 2, 3, 4];
637 let pushed = tx.push_slice(&data);
638 assert_eq!(pushed, 4);
639 for &expected in &data {
640 assert_eq!(rx.try_pop(), Ok(expected));
641 }
642 }
643
644 #[test]
645 fn push_slice_partial_when_full() {
646 let (tx, _rx) = ring(2).unwrap();
647 let _ = tx.push_slice(&[10u32, 20]);
648 let pushed = tx.push_slice(&[30u32, 40]);
649 assert_eq!(pushed, 0);
650 }
651
652 #[test]
653 fn pop_into_slice_all_available() {
654 let (tx, rx) = ring(8).unwrap();
655 for i in 0..4u32 {
656 tx.try_push(i).unwrap();
657 }
658 let mut dst = [0u32; 4];
659 let popped = rx.pop_into_slice(&mut dst);
660 assert_eq!(popped, 4);
661 assert_eq!(dst, [0, 1, 2, 3]);
662 }
663
664 #[test]
665 fn pop_into_slice_empty_buffer() {
666 let (_tx, rx) = ring::<u32>(4).unwrap();
667 let mut dst = [0u32; 4];
668 let popped = rx.pop_into_slice(&mut dst);
669 assert_eq!(popped, 0);
670 assert_eq!(dst, [0u32; 4]);
671 }
672
673 #[test]
674 fn pop_into_slice_partial_dst() {
675 let (tx, rx) = ring(8).unwrap();
676 for i in 0..6u32 {
677 tx.try_push(i).unwrap();
678 }
679 let mut dst = [0u32; 3];
680 let popped = rx.pop_into_slice(&mut dst);
681 assert_eq!(popped, 3);
682 assert_eq!(dst, [0, 1, 2]);
683 assert_eq!(rx.try_pop(), Ok(3));
684 }
685
686 #[test]
687 fn push_pop_slice_roundtrip_concurrent() {
688 let (tx, rx) = ring(256).unwrap();
689 let data: Vec<u32> = (0..1024).collect();
690 let data_clone = data.clone();
691
692 let producer = thread::spawn(move || {
693 let mut sent = 0;
694 while sent < data_clone.len() {
695 sent += tx.push_slice(&data_clone[sent..]);
696 std::hint::spin_loop();
697 }
698 });
699
700 let consumer = thread::spawn(move || {
701 let mut received = Vec::with_capacity(1024);
702 let mut buf = [0u32; 32];
703 while received.len() < 1024 {
704 let n = rx.pop_into_slice(&mut buf);
705 received.extend_from_slice(&buf[..n]);
706 std::hint::spin_loop();
707 }
708 received
709 });
710
711 producer.join().unwrap();
712 let received = consumer.join().unwrap();
713 let expected: Vec<u32> = (0..1024).collect();
714 assert_eq!(received, expected);
715 }
716
717 #[test]
718 fn ring_returns_err_on_non_power_of_two() {
719 assert!(ring::<u32>(3).is_err());
720 assert!(ring::<u32>(0).is_err());
721 }
722
723 #[test]
724 fn ring_returns_ok_on_valid_capacity() {
725 assert!(ring::<u32>(4).is_ok());
726 assert!(ring::<u32>(1).is_ok());
727 }
728
729 #[test]
730 fn is_disconnected_false_while_both_live() {
731 let (tx, rx) = ring::<u32>(4).unwrap();
732 assert!(!tx.is_disconnected());
733 assert!(!rx.is_disconnected());
734 }
735
736 #[test]
737 fn is_disconnected_true_after_drop() {
738 let (tx, rx) = ring::<u32>(4).unwrap();
739 drop(rx);
740 assert!(tx.is_disconnected());
741 }
742
743 #[test]
744 fn producer_drop_signals_disconnected() {
745 let (tx, rx) = ring::<u32>(4).unwrap();
746 drop(tx);
747 assert_eq!(rx.try_pop(), Err(TryRecvError::Disconnected));
748 }
749
750 #[test]
751 fn consumer_drop_signals_disconnected() {
752 let (tx, rx) = ring::<u32>(4).unwrap();
753 drop(rx);
754 assert_eq!(tx.try_push(1), Err(TrySendError::Disconnected(1)));
755 }
756
757 #[test]
758 fn try_pop_returns_empty_when_buffer_empty() {
759 let (_tx, rx) = ring::<u32>(4).unwrap();
760 assert_eq!(rx.try_pop(), Err(TryRecvError::Empty));
761 }
762
763 #[test]
764 fn try_push_returns_full_when_buffer_full() {
765 let (tx, _rx) = ring::<u32>(2).unwrap();
766 tx.try_push(1).unwrap();
767 tx.try_push(2).unwrap();
768 assert_eq!(tx.try_push(3), Err(TrySendError::Full(3)));
769 }
770
771 #[test]
772 fn pop_returns_recv_error_on_disconnect() {
773 let (tx, rx) = ring::<u32>(4).unwrap();
774 drop(tx);
775 assert_eq!(rx.pop(&WaitStrategy::SpinLoop), Err(RecvError));
776 }
777
778 #[test]
779 fn push_returns_send_error_on_disconnect() {
780 let (tx, rx) = ring::<u32>(4).unwrap();
781 drop(rx);
782 assert_eq!(tx.push(42, &WaitStrategy::SpinLoop), Err(SendError(42)));
783 }
784
785 #[test]
786 fn pop_returns_value_before_checking_disconnect() {
787 let (tx, rx) = ring::<u32>(4).unwrap();
788 tx.try_push(99).unwrap();
789 drop(tx);
790 assert_eq!(rx.pop(&WaitStrategy::SpinLoop), Ok(99));
791 assert_eq!(rx.pop(&WaitStrategy::SpinLoop), Err(RecvError));
792 }
793
794 #[test]
795 fn try_pop_drains_buffered_items_after_producer_drop() {
796 let (tx, rx) = ring::<u32>(4).unwrap();
797 tx.try_push(1).unwrap();
798 tx.try_push(2).unwrap();
799 tx.try_push(3).unwrap();
800 drop(tx);
801 assert_eq!(rx.try_pop(), Ok(1));
802 assert_eq!(rx.try_pop(), Ok(2));
803 assert_eq!(rx.try_pop(), Ok(3));
804 assert_eq!(rx.try_pop(), Err(TryRecvError::Disconnected));
805 }
806
807 #[test]
808 fn error_types_exist() {
809 let _: RecvError = RecvError;
810 let _: SendError<u32> = SendError(42);
811 let _: TryRecvError = TryRecvError::Empty;
812 let _: TryRecvError = TryRecvError::Disconnected;
813 let _: TrySendError<u32> = TrySendError::Full(1);
814 let _: TrySendError<u32> = TrySendError::Disconnected(2);
815 }
816
817 #[test]
818 fn wait_strategy_is_copy() {
819 let s = WaitStrategy::Sleep(std::time::Duration::from_millis(1));
820 let _a = s;
821 let _b = s; }
823
824 #[test]
825 fn push_slice_partial_fit() {
826 let (tx, rx) = ring(4).unwrap();
827 tx.push_slice(&[1u32, 2]);
828 let pushed = tx.push_slice(&[3u32, 4, 5, 6]);
829 assert_eq!(pushed, 2);
830 assert_eq!(rx.try_pop(), Ok(1));
831 assert_eq!(rx.try_pop(), Ok(2));
832 assert_eq!(rx.try_pop(), Ok(3));
833 assert_eq!(rx.try_pop(), Ok(4));
834 assert_eq!(rx.try_pop(), Err(TryRecvError::Empty));
835 }
836
837 #[test]
838 #[cfg_attr(miri, ignore)] fn wait_strategy_spin_loop_waits_until_slot_free() {
840 let (tx, rx) = ring(2).unwrap();
841 tx.try_push(1).unwrap();
842 tx.try_push(2).unwrap();
843
844 let consumer = std::thread::spawn(move || {
845 std::thread::sleep(std::time::Duration::from_millis(5));
846 rx.try_pop().unwrap();
847 rx
848 });
849
850 tx.push(3, &WaitStrategy::SpinLoop).unwrap();
851 let rx = consumer.join().unwrap();
852 assert_eq!(rx.try_pop(), Ok(2));
853 assert_eq!(rx.try_pop(), Ok(3));
854 }
855
856 #[test]
857 #[cfg_attr(miri, ignore)] fn wait_strategy_yield_waits_until_value_available() {
859 let (tx, rx) = ring(4).unwrap();
860
861 let producer = std::thread::spawn(move || {
862 std::thread::sleep(std::time::Duration::from_millis(5));
863 tx.try_push(42).unwrap();
864 });
865
866 let value = rx.pop(&WaitStrategy::Yield).unwrap();
867 assert_eq!(value, 42u32);
868 producer.join().unwrap();
869 }
870
871 #[test]
872 fn wait_strategy_sleep_push_pop() {
873 use std::time::Duration;
874 let (tx, rx) = ring(4).unwrap();
875 tx.push(99u64, &WaitStrategy::Sleep(Duration::from_millis(1)))
876 .unwrap();
877 assert_eq!(
878 rx.pop(&WaitStrategy::Sleep(Duration::from_millis(1)))
879 .unwrap(),
880 99u64
881 );
882 }
883
884 #[test]
885 #[cfg_attr(miri, ignore)] fn wait_strategy_sleep_exercises_spin_on_full_buffer() {
887 use std::time::Duration;
888 let (tx, rx) = ring(2).unwrap();
889 tx.try_push(1u32).unwrap();
890 tx.try_push(2u32).unwrap();
891
892 let consumer = std::thread::spawn(move || {
894 std::thread::sleep(Duration::from_millis(10));
895 rx.try_pop().unwrap();
896 rx
897 });
898
899 tx.push(3u32, &WaitStrategy::Sleep(Duration::from_millis(1)))
900 .unwrap();
901 let rx = consumer.join().unwrap();
902 assert_eq!(rx.try_pop(), Ok(2));
903 assert_eq!(rx.try_pop(), Ok(3));
904 }
905
906 #[test]
907 fn push_slice_stops_on_disconnect() {
908 let (tx, rx) = ring::<u32>(8).unwrap();
909 drop(rx);
910 let pushed = tx.push_slice(&[1, 2, 3, 4]);
912 assert_eq!(pushed, 0);
913 assert!(tx.is_disconnected());
914 }
915
916 #[test]
917 fn pop_into_slice_stops_on_disconnect() {
918 let (tx, rx) = ring::<u32>(8).unwrap();
919 tx.try_push(10).unwrap();
920 tx.try_push(20).unwrap();
921 drop(tx);
922 let mut dst = [0u32; 4];
923 let popped = rx.pop_into_slice(&mut dst);
925 assert_eq!(popped, 2);
926 assert_eq!(dst[0], 10);
927 assert_eq!(dst[1], 20);
928 }
929
930 #[test]
931 fn both_halves_drop_simultaneously() {
932 let (tx, rx) = ring::<u32>(4).unwrap();
934 let t1 = std::thread::spawn(move || drop(tx));
935 let t2 = std::thread::spawn(move || drop(rx));
936 t1.join().unwrap();
937 t2.join().unwrap();
938 }
939
940 #[test]
941 fn ring_capacity_one_wraps_correctly() {
942 let (tx, rx) = ring::<u32>(1).unwrap();
943 for i in 0..4u32 {
944 tx.try_push(i).unwrap();
945 assert_eq!(rx.try_pop(), Ok(i));
946 }
947 assert_eq!(rx.try_pop(), Err(TryRecvError::Empty));
948 }
949
950 #[test]
951 fn push_slice_empty_is_noop() {
952 let (tx, rx) = ring::<u32>(4).unwrap();
953 assert_eq!(tx.push_slice(&[]), 0);
954 assert_eq!(rx.try_pop(), Err(TryRecvError::Empty));
955 }
956
957 #[test]
958 fn pop_into_slice_empty_is_noop() {
959 let (tx, rx) = ring::<u32>(4).unwrap();
960 tx.try_push(1).unwrap();
961 assert_eq!(rx.pop_into_slice(&mut []), 0);
962 assert_eq!(rx.try_pop(), Ok(1));
963 }
964}
965
966#[cfg(loom)]
967mod loom_tests {
968 use super::ring;
969 use loom::thread;
970
971 #[test]
977 fn push_then_pop_all_interleavings() {
978 loom::model(|| {
979 let (tx, rx) = ring(2).expect("valid capacity");
980
981 let producer = thread::spawn(move || {
982 tx.try_push(42usize).ok();
983 });
984
985 let consumer = thread::spawn(move || rx.try_pop());
986
987 producer.join().unwrap();
988 let _result = consumer.join().unwrap();
989 });
990 }
991
992 #[test]
995 fn push_pop_two_items() {
996 loom::model(|| {
997 let (tx, rx) = ring(2).expect("valid capacity");
998
999 let producer = thread::spawn(move || {
1000 loop {
1001 match tx.try_push(1usize) {
1002 Ok(()) => break,
1003 Err(_) => loom::hint::spin_loop(),
1004 }
1005 }
1006 loop {
1007 match tx.try_push(2usize) {
1008 Ok(()) => break,
1009 Err(_) => loom::hint::spin_loop(),
1010 }
1011 }
1012 });
1013
1014 let consumer = thread::spawn(move || {
1015 let mut got = Vec::new();
1016 while got.len() < 2 {
1017 match rx.try_pop() {
1018 Ok(v) => got.push(v),
1019 Err(_) => loom::hint::spin_loop(),
1020 }
1021 }
1022 got
1023 });
1024
1025 producer.join().unwrap();
1026 let got = consumer.join().unwrap();
1027 assert_eq!(got, vec![1, 2]);
1028 });
1029 }
1030}