1use std::cell::UnsafeCell;
11use std::sync::atomic::{AtomicUsize, Ordering};
12
13#[repr(align(64))]
15#[derive(Default)]
16struct Padded(AtomicUsize);
17
18pub struct SpscRing<T, const CAP: usize> {
20 mask: usize,
21 head: Padded, tail: Padded, slots: Box<[Slot<T>]>,
24}
25
26struct Slot<T> {
27 sequence: AtomicUsize,
28 value: UnsafeCell<Option<T>>,
29}
30
31unsafe impl<T: Send, const CAP: usize> Send for SpscRing<T, CAP> {}
33unsafe impl<T: Send, const CAP: usize> Sync for SpscRing<T, CAP> {}
36
37impl<T, const CAP: usize> SpscRing<T, CAP> {
38 #[must_use]
40 pub fn new() -> Self {
41 const {
42 assert!(CAP.is_power_of_two(), "CAP must be a power of two");
43 }
44 let slots = (0..CAP)
45 .map(|i| Slot {
46 sequence: AtomicUsize::new(i),
47 value: UnsafeCell::new(None),
48 })
49 .collect::<Vec<_>>();
50 Self {
51 mask: CAP - 1,
52 head: Padded::default(),
53 tail: Padded::default(),
54 slots: slots.into_boxed_slice(),
55 }
56 }
57
58 #[inline]
60 pub fn try_push(&self, value: T) -> Option<T> {
61 let pos = self.head.0.load(Ordering::Relaxed);
62 let slot = &self.slots[pos & self.mask];
63 if slot.sequence.load(Ordering::Acquire) != pos {
64 return Some(value); }
66 unsafe {
69 *slot.value.get() = Some(value);
70 }
71 slot.sequence.store(pos.wrapping_add(1), Ordering::Release);
72 self.head.0.store(pos.wrapping_add(1), Ordering::Relaxed);
73 None
74 }
75
76 #[inline]
78 pub fn try_pop(&self) -> Option<T> {
79 let pos = self.tail.0.load(Ordering::Relaxed);
80 let slot = &self.slots[pos & self.mask];
81 if slot.sequence.load(Ordering::Acquire) != pos + 1 {
82 return None; }
84 let value = unsafe { (*slot.value.get()).take() };
87 slot.sequence
88 .store(pos.wrapping_add(CAP), Ordering::Release);
89 self.tail.0.store(pos.wrapping_add(1), Ordering::Relaxed);
90 value
91 }
92
93 #[must_use]
95 pub fn len(&self) -> usize {
96 self.head
97 .0
98 .load(Ordering::Relaxed)
99 .wrapping_sub(self.tail.0.load(Ordering::Relaxed))
100 }
101
102 #[must_use]
104 pub fn is_empty(&self) -> bool {
105 self.len() == 0
106 }
107}
108
109impl<T, const CAP: usize> Default for SpscRing<T, CAP> {
110 fn default() -> Self {
111 Self::new()
112 }
113}
114
115#[must_use]
117pub fn channel<T, const CAP: usize>() -> (SpscSender<T, CAP>, SpscReceiver<T, CAP>) {
118 let ring = std::sync::Arc::new(SpscRing::new());
119 (
120 SpscSender {
121 ring: std::sync::Arc::clone(&ring),
122 },
123 SpscReceiver { ring },
124 )
125}
126
127pub struct SpscSender<T, const CAP: usize> {
129 ring: std::sync::Arc<SpscRing<T, CAP>>,
130}
131
132impl<T, const CAP: usize> SpscSender<T, CAP> {
133 #[inline]
138 pub fn send(&self, value: T) -> Result<(), T> {
139 match self.ring.try_push(value) {
140 None => Ok(()),
141 Some(back) => Err(back),
142 }
143 }
144}
145
146pub struct SpscReceiver<T, const CAP: usize> {
148 ring: std::sync::Arc<SpscRing<T, CAP>>,
149}
150
151impl<T, const CAP: usize> SpscReceiver<T, CAP> {
152 #[inline]
154 pub fn recv(&self) -> Option<T> {
155 self.ring.try_pop()
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162
163 #[test]
164 fn spsc_basic() {
165 let (tx, rx) = channel::<u64, 4>();
166 tx.send(1).expect("fits");
167 tx.send(2).expect("fits");
168 assert_eq!(rx.recv(), Some(1));
169 assert_eq!(rx.recv(), Some(2));
170 assert_eq!(rx.recv(), None);
171 }
172
173 #[test]
174 fn full_returns_back() {
175 let (tx, rx) = channel::<u8, 2>();
176 tx.send(1).unwrap();
177 tx.send(2).unwrap();
178 assert_eq!(tx.send(3), Err(3));
179 assert_eq!(rx.recv(), Some(1));
180 tx.send(3).expect("space now");
181 assert_eq!(rx.recv(), Some(2));
182 assert_eq!(rx.recv(), Some(3));
183 }
184
185 #[test]
186 fn wraparound() {
187 let (tx, rx) = channel::<usize, 4>();
188 for i in 0..10_000usize {
189 tx.send(i).expect("loop drains");
190 assert_eq!(rx.recv(), Some(i));
191 }
192 }
193
194 #[test]
195 fn cross_thread_sum() {
196 let (tx, rx) = channel::<u64, 1024>();
197 std::thread::scope(|s| {
198 s.spawn(|| {
199 for i in 0..100_000u64 {
200 while tx.send(i).is_err() {
201 std::hint::spin_loop();
202 }
203 }
204 });
205 let got: u64 = (0..100_000u64)
206 .map(|_| {
207 loop {
208 if let Some(v) = rx.recv() {
209 break v;
210 } else {
211 std::hint::spin_loop();
212 }
213 }
214 })
215 .sum();
216 assert_eq!(got, 100_000u64 * 99_999 / 2);
217 });
218 }
219}