Skip to main content

ringbuf/rb/
shared.rs

1use super::{
2    macros::rb_impl_init,
3    utils::{assert_capacity, ranges},
4};
5#[cfg(feature = "alloc")]
6use crate::traits::Split;
7use crate::{
8    storage::Storage,
9    traits::{
10        Observer, RingBuffer, SplitRef,
11        consumer::{Consumer, impl_consumer_traits},
12        producer::{Producer, impl_producer_traits},
13    },
14    wrap::{CachingCons, CachingProd},
15};
16use core::{
17    mem::{ManuallyDrop, MaybeUninit},
18    num::NonZeroUsize,
19    ptr,
20};
21use crossbeam_utils::CachePadded;
22
23#[cfg(not(feature = "portable-atomic"))]
24use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
25#[cfg(feature = "portable-atomic")]
26use portable_atomic::{AtomicBool, AtomicUsize, Ordering};
27#[cfg(feature = "alloc")]
28use {crate::alias::Arc, alloc::boxed::Box};
29
30/// Ring buffer that can be shared between threads.
31///
32/// Note that there is no explicit requirement of `T: Send`. Instead ring buffer will work just fine even with `T: !Send`
33/// until you try to send its producer or consumer to another thread.
34#[cfg_attr(
35    feature = "std",
36    doc = r##"
37```
38use std::thread;
39use ringbuf::{SharedRb, storage::Heap, traits::*};
40
41let rb = SharedRb::<Heap<i32>>::new(256);
42let (mut prod, mut cons) = rb.split();
43thread::spawn(move || {
44    prod.try_push(123).unwrap();
45})
46.join();
47thread::spawn(move || {
48    assert_eq!(cons.try_pop().unwrap(), 123);
49})
50.join();
51```
52"##
53)]
54pub struct SharedRb<S: Storage + ?Sized> {
55    read_index: CachePadded<AtomicUsize>,
56    write_index: CachePadded<AtomicUsize>,
57    read_held: AtomicBool,
58    write_held: AtomicBool,
59    storage: S,
60}
61
62impl<S: Storage> SharedRb<S> {
63    /// Constructs ring buffer from storage and indices.
64    ///
65    /// Panics if the storage is empty or its length exceeds `usize::MAX / 2`.
66    ///
67    /// # Safety
68    ///
69    /// The items in storage inside `read..write` range must be initialized, items outside this range must be uninitialized.
70    /// `read` and `write` positions must be valid (see implementation details).
71    pub unsafe fn from_raw_parts(storage: S, read: usize, write: usize) -> Self {
72        assert_capacity(storage.len());
73        Self {
74            storage,
75            read_index: CachePadded::new(AtomicUsize::new(read)),
76            write_index: CachePadded::new(AtomicUsize::new(write)),
77            read_held: AtomicBool::new(false),
78            write_held: AtomicBool::new(false),
79        }
80    }
81    /// Destructures ring buffer into underlying storage and `read` and `write` indices.
82    ///
83    /// # Safety
84    ///
85    /// Initialized contents of the storage must be properly dropped.
86    pub unsafe fn into_raw_parts(self) -> (S, usize, usize) {
87        let this = ManuallyDrop::new(self);
88        (unsafe { ptr::read(&this.storage) }, this.read_index(), this.write_index())
89    }
90}
91
92impl<S: Storage + ?Sized> Observer for SharedRb<S> {
93    type Item = S::Item;
94
95    #[inline]
96    fn capacity(&self) -> NonZeroUsize {
97        unsafe { NonZeroUsize::new_unchecked(self.storage.len()) }
98    }
99
100    #[inline]
101    fn read_index(&self) -> usize {
102        self.read_index.load(Ordering::Acquire)
103    }
104    #[inline]
105    fn write_index(&self) -> usize {
106        self.write_index.load(Ordering::Acquire)
107    }
108
109    unsafe fn unsafe_slices(&self, start: usize, end: usize) -> (&[MaybeUninit<S::Item>], &[MaybeUninit<S::Item>]) {
110        let (first, second) = ranges(self.capacity(), start, end);
111        unsafe { (self.storage.slice(first), self.storage.slice(second)) }
112    }
113    unsafe fn unsafe_slices_mut(&self, start: usize, end: usize) -> (&mut [MaybeUninit<S::Item>], &mut [MaybeUninit<S::Item>]) {
114        let (first, second) = ranges(self.capacity(), start, end);
115        unsafe { (self.storage.slice_mut(first), self.storage.slice_mut(second)) }
116    }
117
118    #[inline]
119    fn read_is_held(&self) -> bool {
120        self.read_held.load(Ordering::Acquire)
121    }
122    #[inline]
123    fn write_is_held(&self) -> bool {
124        self.write_held.load(Ordering::Acquire)
125    }
126}
127
128impl<S: Storage + ?Sized> Producer for SharedRb<S> {
129    #[inline]
130    unsafe fn set_write_index(&self, value: usize) {
131        self.write_index.store(value, Ordering::Release);
132    }
133}
134
135impl<S: Storage + ?Sized> Consumer for SharedRb<S> {
136    #[inline]
137    unsafe fn set_read_index(&self, value: usize) {
138        self.read_index.store(value, Ordering::Release);
139    }
140}
141
142impl<S: Storage + ?Sized> RingBuffer for SharedRb<S> {
143    #[inline]
144    unsafe fn hold_read(&self, flag: bool) -> bool {
145        self.read_held.swap(flag, Ordering::AcqRel)
146    }
147    #[inline]
148    unsafe fn hold_write(&self, flag: bool) -> bool {
149        self.write_held.swap(flag, Ordering::AcqRel)
150    }
151}
152
153impl<S: Storage + ?Sized> Drop for SharedRb<S> {
154    fn drop(&mut self) {
155        self.clear();
156    }
157}
158
159#[cfg(feature = "alloc")]
160impl<S: Storage> Split for SharedRb<S> {
161    type Prod = CachingProd<Arc<Self>>;
162    type Cons = CachingCons<Arc<Self>>;
163
164    fn split(self) -> (Self::Prod, Self::Cons) {
165        Arc::new(self).split()
166    }
167}
168#[cfg(feature = "alloc")]
169impl<S: Storage + ?Sized> Split for Arc<SharedRb<S>> {
170    type Prod = CachingProd<Self>;
171    type Cons = CachingCons<Self>;
172
173    fn split(self) -> (Self::Prod, Self::Cons) {
174        (CachingProd::new(self.clone()), CachingCons::new(self))
175    }
176}
177#[cfg(feature = "alloc")]
178impl<S: Storage + ?Sized> Split for Box<SharedRb<S>> {
179    type Prod = CachingProd<Arc<SharedRb<S>>>;
180    type Cons = CachingCons<Arc<SharedRb<S>>>;
181
182    fn split(self) -> (Self::Prod, Self::Cons) {
183        Arc::<SharedRb<S>>::from(self).split()
184    }
185}
186impl<S: Storage + ?Sized> SplitRef for SharedRb<S> {
187    type RefProd<'a>
188        = CachingProd<&'a Self>
189    where
190        Self: 'a;
191    type RefCons<'a>
192        = CachingCons<&'a Self>
193    where
194        Self: 'a;
195
196    fn split_ref(&mut self) -> (Self::RefProd<'_>, Self::RefCons<'_>) {
197        (CachingProd::new(self), CachingCons::new(self))
198    }
199}
200
201rb_impl_init!(SharedRb);
202
203impl_producer_traits!(SharedRb<S: Storage>);
204impl_consumer_traits!(SharedRb<S: Storage>);
205
206impl<S: Storage + ?Sized> AsRef<Self> for SharedRb<S> {
207    fn as_ref(&self) -> &Self {
208        self
209    }
210}
211impl<S: Storage + ?Sized> AsMut<Self> for SharedRb<S> {
212    fn as_mut(&mut self) -> &mut Self {
213        self
214    }
215}