llt_rs/ring_buffer/mod.rs
1#![doc = include_str!("README.md")]
2
3use crossbeam_utils::CachePadded;
4use std::cell::UnsafeCell;
5use std::mem::MaybeUninit;
6use std::sync::atomic::{AtomicUsize, Ordering};
7
8/// A Single-Producer, Single-Consumer (SPSC) lock free ring buffer.
9/// This queue is "wait-free" (bounded time) for both producer and consumer.
10/// It does not block, but return `Err` or `None` if the queue is full or empty.
11pub struct RingBuffer<T> {
12 ///The buffer, allocated on the heap
13 /// We use `UnsafeCell` for interior mutability (to write from `&self`).
14 /// We use `MaybeUninit` to store uninitialized data and take ownership
15 /// of `T's` when we `recv`
16 buffer: Box<[UnsafeCell<MaybeUninit<T>>]>,
17
18 /// The capacity of the buffer, Must be a power of 2 (an optimization that allows us to use bit trick instead of modulo)
19 cap: usize,
20
21 /// The `head` counter.
22 /// This is where the producer will write the *next* item.
23 /// Only the producer modifies this.
24 /// Padded to prevent false sharing with `tail`.
25 head: CachePadded<AtomicUsize>,
26
27 ///The "tail" counter
28 /// This is where the consumer will read the *next* item from.
29 /// Only the consumer modifies this
30 /// Padded to prevent false sharing with `head`.
31 tail: CachePadded<AtomicUsize>,
32}
33
34/// We can safely send the RingBuffer to other threads if T is Send
35/// `Unsafe` is not `Sync` BUT WE *know* we are only accessing
36/// the buffer safely from the *single* producer and *single* consumer.
37/// The `head` and `tail` atomics prevent reading/writing the same slot.
38unsafe impl<T: Send> Sync for RingBuffer<T> {}
39unsafe impl<T: Send> Send for RingBuffer<T> {}
40
41impl<T> RingBuffer<T> {
42 /// Creates a new SPSC ring buffer with *at least* the given capacity
43 /// The actual capacity will be rounded up to the next power of 2.
44 pub fn new(capacity: usize) -> Self {
45 // Round up to the next power of 2
46 //this allows us to replace a slow modulo with fast BITWISE-AND
47 let cap = capacity.next_power_of_two();
48 //Create a Vec and fill it with uninitialized data
49 let mut buffer = Vec::with_capacity(cap);
50 for _ in 0..cap {
51 buffer.push(UnsafeCell::new(MaybeUninit::uninit()));
52 }
53
54 // Convert the Vec to a Box<[]>
55 let buffer = buffer.into_boxed_slice();
56
57 Self {
58 buffer,
59 cap,
60 head: CachePadded::new(AtomicUsize::new(0)),
61 tail: CachePadded::new(AtomicUsize::new(0)),
62 }
63 }
64
65 /// Returns the capacity of the ring buffer.
66 pub fn capacity(&self) -> usize {
67 self.cap
68 }
69
70 ///Returns the number of items currently in the buffer.
71 /// This is a snapshot and maybe out of date immediately.
72 pub fn len(&self) -> usize {
73 let head = self.head.load(Ordering::Relaxed);
74 let tail = self.tail.load(Ordering::Relaxed);
75 head.wrapping_sub(tail)
76 }
77 ///Returns true if the buffer is empty.
78 pub fn is_empty(&self) -> bool {
79 self.len() == 0
80 }
81
82 /// sends a item into a buffer
83 ///
84 /// Fails if the buffer is full, returning an `Err(item)`.
85 /// This is the *Producer* method.
86 pub fn send(&self, item: T) -> Result<(), T> {
87 // Load the current head and tail.
88 // `head` can be Relaxed because only *we* can change it.
89 // `tail` must be `Acquire` to "see" the consumer's `Release`
90 // store, which signals that a slot has been freed.
91 let head = self.head.load(Ordering::Relaxed);
92 let tail = self.tail.load(Ordering::Acquire);
93 //Check if the buffer is full
94 // `wrapping_sub` handles counter wrap-around.
95 if head.wrapping_sub(tail) == self.cap {
96 return Err(item);
97 }
98 // Calculate the slot index using the power-of-2 bit-trick.
99 // This is much faster than `head % self.cap`.
100 let slot_idx = head & (self.cap - 1);
101
102 // SAFETY:
103 // 1. `&self` is_ok because `UnsafeCell` provides interior mutability.
104 // 2. The `is_full` check (head - tail == cap) guarantees that
105 // this slot is "owned" by the producer. The consumer will
106 // *never* read from this slot until we increment `head`.
107 // 3. We are writing a `MaybeUninit::new(item)` which is valid.
108 unsafe {
109 let slot_ptr = self.buffer[slot_idx].get();
110 (*slot_ptr).write(item);
111 }
112 // "Publish" the write.
113 // We use `Release` to ensure that the data write (above)
114 // is *not* reordered *after* this store. This makes the
115 // data visible to the consumer's `Acquire` load.
116 self.head.store(head.wrapping_add(1), Ordering::Release);
117 Ok(())
118 }
119 pub fn recv(&self) -> Option<T> {
120 //Load the current head and tail.
121 // `tail` can be Relaxed because only *we* change it.
122 // `head` must be `Acquire` to "see" the producer's `Release`
123 // store, which signals that data is available.
124 let tail = self.tail.load(Ordering::Relaxed);
125 let head = self.head.load(Ordering::Acquire);
126
127 //Check if the buffer is empty
128 if tail == head {
129 return None;
130 }
131
132 let slot_idx = tail & (self.cap - 1);
133 //Calculate the slot index.
134 //SAFETY.
135 //1. `&self` is ok because `UnsafeCell`.
136 //2. The `is_empty` check (head == tail) guarantees that
137 // this slot *contains data* put there by the producer.
138 //3. The producer's `Release` store (on `head`) ensures that
139 // the data write is visible *before* we saw the new `head`.
140 // 4 . `assume_init_read` takes ownership of the `T`, leaving
141 // The `MaybeUninit` in a "uninitialized" state which is fine
142
143 let item = unsafe {
144 let slot_ptr = self.buffer[slot_idx].get();
145 (*slot_ptr).assume_init_read()
146 };
147 // "Publish" that we have a freed up a slot.
148 // We use `Release` to ensure that our "take" (the read)
149 // is visible to the producer's `Acquire` load of `tail`.
150
151 self.tail.store(tail.wrapping_add(1), Ordering::Release);
152
153 Some(item)
154 }
155}
156
157/// We must implement Drop to clean up any `T` a left in the buffer.
158impl<T> Drop for RingBuffer<T> {
159 fn drop(&mut self) {
160 //We are in `&MUT self`, so no other threads can be accessing
161 // the buffer, We can use `Relaxed` ordering;
162 let mut tail = self.tail.load(Ordering::Relaxed);
163 let head = self.head.load(Ordering::Relaxed);
164 while tail != head {
165 let slot_idx = tail & (self.cap - 1);
166 //SAFETY:
167 //1. We have `&mut self`, so no other thread is racing.
168 //2. We are iterating from `tail` to `head` which are the
169 // slots that contain initialized data.
170 // 3 `drop_in_place` calls the destructor for `T`
171 unsafe {
172 let slot_ptr = self.buffer[slot_idx].get();
173 //Use `as_mut` to get `&mut MaybeUninit<T>`
174 //and then `drop_in_place` on its contents.
175 std::ptr::drop_in_place((*slot_ptr).as_mut_ptr());
176 }
177 tail = tail.wrapping_add(1);
178 }
179 }
180}
181#[cfg(test)]
182mod tests {
183 use super::*;
184 use std::sync::Arc;
185 use std::thread;
186
187 #[test]
188 fn test_single_thread_send_recv() {
189 let rb = RingBuffer::new(4);
190 assert_eq!(rb.capacity(), 4); // 4 is a power of 2
191
192 rb.send("hello").unwrap();
193 rb.send("world").unwrap();
194
195 assert_eq!(rb.len(), 2);
196
197 assert_eq!(rb.recv(), Some("hello"));
198 assert_eq!(rb.recv(), Some("world"));
199 assert_eq!(rb.recv(), None);
200 assert_eq!(rb.len(), 0);
201 }
202
203 #[test]
204 fn test_full_and_empty() {
205 let rb = RingBuffer::new(2);
206 assert_eq!(rb.capacity(), 2);
207
208 rb.send(1).unwrap();
209 rb.send(2).unwrap();
210
211 // Buffer is full
212 assert_eq!(rb.send(3), Err(3));
213 assert_eq!(rb.len(), 2);
214
215 // Receive one
216 assert_eq!(rb.recv(), Some(1));
217 assert_eq!(rb.len(), 1);
218
219 // Now we can send again
220 rb.send(3).unwrap();
221 assert_eq!(rb.len(), 2);
222
223 assert_eq!(rb.recv(), Some(2));
224 assert_eq!(rb.recv(), Some(3));
225 assert_eq!(rb.recv(), None);
226 assert_eq!(rb.len(), 0);
227 }
228
229 #[test]
230 fn test_multi_thread_spsc() {
231 // Use Arc to share the RingBuffer between threads
232 let rb = Arc::new(RingBuffer::new(1024));
233 let num_items = 1_000_000;
234
235 let producer_rb = rb.clone();
236 let producer_thread = thread::spawn(move || {
237 for i in 0..num_items {
238 // Spin-wait if the buffer is full
239 // FIX: Use `_item` to mark the variable as intentionally unused.
240 while let Err(_item) = producer_rb.send(i) {
241 thread::yield_now();
242 }
243 }
244 });
245
246 let consumer_rb = rb.clone();
247 let consumer_thread = thread::spawn(move || {
248 let mut received_count = 0;
249 let mut next_expected = 0;
250 while received_count < num_items {
251 // Spin-wait if the buffer is empty
252 match consumer_rb.recv() {
253 Some(item) => {
254 assert_eq!(item, next_expected);
255 next_expected += 1;
256 received_count += 1;
257 }
258 None => {
259 thread::yield_now();
260 }
261 }
262 }
263 });
264
265 producer_thread.join().unwrap();
266 consumer_thread.join().unwrap();
267 }
268
269 #[test]
270 fn test_drop_cleanup() {
271 // A simple type to track drops
272 static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
273 // FIX: Add `#[derive(Debug)]` so that `unwrap()` on `Result` can compile.
274 #[derive(Debug)]
275 struct Dropper;
276 impl Drop for Dropper {
277 fn drop(&mut self) {
278 DROP_COUNT.fetch_add(1, Ordering::SeqCst);
279 }
280 }
281
282 DROP_COUNT.store(0, Ordering::SeqCst);
283
284 // Create a new scope
285 {
286 let rb = RingBuffer::new(8);
287 rb.send(Dropper).unwrap();
288 rb.send(Dropper).unwrap();
289 rb.send(Dropper).unwrap();
290
291 // Receive one
292 // FIX: Introduce a new scope to force `_d` to be
293 // dropped immediately after it's received.
294 {
295 let _d = rb.recv().unwrap();
296 } // _d (and the Dropper it holds) is dropped right here.
297
298 // at this point, 1 should be dropped
299 assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 1);
300
301 // two items are still in the buffer
302 // when `rb` goes out of scope, it should be dropped
303 }
304
305 // The Drop implementation of RingBuffer should have
306 // dropped the remaining 2 items.
307 assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 3);
308 }
309}