1use crossbeam_utils::CachePadded;
7use std::cell::{Cell, UnsafeCell};
8use std::marker::PhantomData;
9use std::mem::MaybeUninit;
10use std::sync::atomic::{AtomicUsize, Ordering};
11use std::sync::Arc;
12
13struct RingBuffer<T, const CAP: usize> {
14 head: CachePadded<AtomicUsize>,
16 tail: CachePadded<AtomicUsize>,
18 slots: Box<[UnsafeCell<MaybeUninit<T>>]>,
20}
21
22unsafe impl<T: Send, const CAP: usize> Send for RingBuffer<T, CAP> {}
26
27unsafe impl<T: Send, const CAP: usize> Sync for RingBuffer<T, CAP> {}
30
31impl<T, const CAP: usize> Drop for RingBuffer<T, CAP> {
32 fn drop(&mut self) {
33 if std::mem::needs_drop::<T>() {
34 let head = self.head.load(Ordering::Relaxed);
35 let tail = self.tail.load(Ordering::Relaxed);
36 let count = head.wrapping_sub(tail);
37 for i in 0..count {
38 let idx = tail.wrapping_add(i) & (CAP - 1);
39 unsafe {
43 let slot = self.slots[idx].get();
44 (*slot).assume_init_drop();
45 }
46 }
47 }
48 }
49}
50
51pub struct SpscProducer<T, const CAP: usize> {
53 inner: Arc<RingBuffer<T, CAP>>,
54 cached_tail: Cell<usize>,
55 _not_sync: PhantomData<Cell<()>>,
56}
57
58pub struct SpscConsumer<T, const CAP: usize> {
60 inner: Arc<RingBuffer<T, CAP>>,
61 cached_head: Cell<usize>,
62 _not_sync: PhantomData<Cell<()>>,
63}
64
65impl<T, const CAP: usize> std::fmt::Debug for SpscProducer<T, CAP> {
66 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 f.debug_struct("SpscProducer")
68 .field("capacity", &CAP)
69 .finish()
70 }
71}
72
73impl<T, const CAP: usize> std::fmt::Debug for SpscConsumer<T, CAP> {
74 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75 f.debug_struct("SpscConsumer")
76 .field("capacity", &CAP)
77 .finish()
78 }
79}
80
81pub fn spsc_pair<T, const CAP: usize>() -> (SpscProducer<T, CAP>, SpscConsumer<T, CAP>) {
86 assert!(CAP > 0, "capacity must be greater than zero");
87 assert!(CAP.is_power_of_two(), "capacity must be a power of two");
88
89 let mut v: Vec<UnsafeCell<MaybeUninit<T>>> = Vec::with_capacity(CAP);
90 unsafe {
94 v.set_len(CAP);
95 }
96 let slots = v.into_boxed_slice();
97
98 let inner = Arc::new(RingBuffer {
99 head: CachePadded::new(AtomicUsize::new(0)),
100 tail: CachePadded::new(AtomicUsize::new(0)),
101 slots,
102 });
103
104 let producer = SpscProducer {
105 inner: Arc::clone(&inner),
106 cached_tail: Cell::new(0),
107 _not_sync: PhantomData,
108 };
109
110 let consumer = SpscConsumer {
111 inner,
112 cached_head: Cell::new(0),
113 _not_sync: PhantomData,
114 };
115
116 (producer, consumer)
117}
118
119impl<T, const CAP: usize> SpscProducer<T, CAP> {
120 pub fn push(&self, item: T) -> Result<(), T> {
124 let head = self.inner.head.load(Ordering::Relaxed);
125 let mut tail = self.cached_tail.get();
126 if head.wrapping_sub(tail) >= CAP {
127 tail = self.inner.tail.load(Ordering::Acquire);
128 self.cached_tail.set(tail);
129 if head.wrapping_sub(tail) >= CAP {
130 return Err(item);
131 }
132 }
133
134 let idx = head & (CAP - 1);
135 unsafe {
142 let slot = self.inner.slots[idx].get();
143 (*slot).write(item);
144 }
145
146 self.inner
147 .head
148 .store(head.wrapping_add(1), Ordering::Release);
149 Ok(())
150 }
151
152 pub fn capacity(&self) -> usize {
154 CAP
155 }
156}
157
158impl<T, const CAP: usize> SpscConsumer<T, CAP> {
159 pub fn pop(&self) -> Option<T> {
163 let tail = self.inner.tail.load(Ordering::Relaxed);
164 let mut head = self.cached_head.get();
165 if head == tail {
166 head = self.inner.head.load(Ordering::Acquire);
167 self.cached_head.set(head);
168 if head == tail {
169 return None;
170 }
171 }
172
173 let idx = tail & (CAP - 1);
174 let item = unsafe {
180 let slot = self.inner.slots[idx].get();
181 (*slot).assume_init_read()
182 };
183
184 self.inner
185 .tail
186 .store(tail.wrapping_add(1), Ordering::Release);
187 Some(item)
188 }
189
190 pub fn drain_batch(&self, out: &mut Vec<T>, max: usize) -> usize {
194 if max == 0 {
195 return 0;
196 }
197
198 let tail = self.inner.tail.load(Ordering::Relaxed);
199 let mut head = self.cached_head.get();
200 if head.wrapping_sub(tail) < max {
201 head = self.inner.head.load(Ordering::Acquire);
202 self.cached_head.set(head);
203 }
204
205 let available = head.wrapping_sub(tail);
206 let to_drain = available.min(max);
207 if to_drain == 0 {
208 return 0;
209 }
210
211 out.reserve(to_drain);
212 for i in 0..to_drain {
213 let idx = tail.wrapping_add(i) & (CAP - 1);
214 let item = unsafe {
218 let slot = self.inner.slots[idx].get();
219 (*slot).assume_init_read()
220 };
221 out.push(item);
222 }
223
224 self.inner
225 .tail
226 .store(tail.wrapping_add(to_drain), Ordering::Release);
227 to_drain
228 }
229
230 pub fn is_empty(&self) -> bool {
232 let tail = self.inner.tail.load(Ordering::Relaxed);
233 let head = self.inner.head.load(Ordering::Acquire);
234 self.cached_head.set(head);
235 head == tail
236 }
237}
238
239impl<T, const CAP: usize> Drop for SpscConsumer<T, CAP> {
240 fn drop(&mut self) {
241 while self.pop().is_some() {}
242 }
243}