diff --git a/async/src/rb.rs b/async/src/rb.rs index c534636..1693775 100644 --- a/async/src/rb.rs +++ b/async/src/rb.rs @@ -6,7 +6,7 @@ use futures::task::AtomicWaker; #[cfg(feature = "alloc")] use ringbuf::traits::Split; use ringbuf::{ - rb::traits::RbRef, + rb::RbRef, storage::Storage, traits::{Consumer, Observer, Producer, RingBuffer, SplitRef}, SharedRb, diff --git a/async/src/wrap/cons.rs b/async/src/wrap/cons.rs index c544180..af949ef 100644 --- a/async/src/wrap/cons.rs +++ b/async/src/wrap/cons.rs @@ -11,7 +11,7 @@ use ringbuf::{ consumer::{Consumer, DelegateConsumer}, Observer, }, - wrap::traits::Wrap, + wrap::Wrap, }; #[cfg(feature = "std")] use std::io; diff --git a/async/src/wrap/mod.rs b/async/src/wrap/mod.rs index f2b9225..4fb0321 100644 --- a/async/src/wrap/mod.rs +++ b/async/src/wrap/mod.rs @@ -4,7 +4,7 @@ mod prod; use crate::rb::AsyncRbRef; use ringbuf::{ traits::{observer::DelegateObserver, Based}, - wrap::{direct::Direct, traits::Wrap}, + wrap::{direct::Direct, Wrap}, Obs, }; diff --git a/async/src/wrap/prod.rs b/async/src/wrap/prod.rs index 460fae7..d5f36a1 100644 --- a/async/src/wrap/prod.rs +++ b/async/src/wrap/prod.rs @@ -13,7 +13,7 @@ use ringbuf::{ producer::{DelegateProducer, Producer}, Observer, }, - wrap::traits::Wrap, + wrap::Wrap, }; #[cfg(feature = "std")] use std::io; diff --git a/blocking/src/rb.rs b/blocking/src/rb.rs index 02a001b..f6dabdb 100644 --- a/blocking/src/rb.rs +++ b/blocking/src/rb.rs @@ -7,7 +7,7 @@ use core::{mem::MaybeUninit, num::NonZeroUsize}; #[cfg(feature = "alloc")] use ringbuf::traits::Split; use ringbuf::{ - rb::traits::RbRef, + rb::RbRef, storage::Storage, traits::{Consumer, Observer, Producer, RingBuffer, SplitRef}, SharedRb, diff --git a/blocking/src/wrap/cons.rs b/blocking/src/wrap/cons.rs index e3666f3..42eb25e 100644 --- a/blocking/src/wrap/cons.rs +++ b/blocking/src/wrap/cons.rs @@ -5,7 +5,7 @@ use core::time::Duration; use ringbuf::traits::Based; use ringbuf::{ traits::{consumer::DelegateConsumer, observer::DelegateObserver, Consumer, Observer}, - wrap::traits::Wrap, + wrap::Wrap, }; #[cfg(feature = "std")] use std::io; diff --git a/blocking/src/wrap/mod.rs b/blocking/src/wrap/mod.rs index 412c432..09c53ed 100644 --- a/blocking/src/wrap/mod.rs +++ b/blocking/src/wrap/mod.rs @@ -5,7 +5,7 @@ use crate::rb::BlockingRbRef; use core::time::Duration; use ringbuf::{ traits::Based, - wrap::{caching::Caching, traits::Wrap}, + wrap::{caching::Caching, Wrap}, Obs, }; diff --git a/blocking/src/wrap/prod.rs b/blocking/src/wrap/prod.rs index a742ab4..c54ab78 100644 --- a/blocking/src/wrap/prod.rs +++ b/blocking/src/wrap/prod.rs @@ -5,7 +5,7 @@ use core::time::Duration; use ringbuf::traits::Based; use ringbuf::{ traits::{observer::DelegateObserver, producer::DelegateProducer, Observer, Producer}, - wrap::traits::Wrap, + wrap::Wrap, }; #[cfg(feature = "std")] use std::io; diff --git a/src/lib.rs b/src/lib.rs index bee86f6..0164469 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,8 @@ +//! Lock-free SPSC FIFO ring buffer with direct access to inner data. +//! +//! For implementation details see [`RingBuffer`](`traits::RingBuffer`) description. #![no_std] #![allow(clippy::type_complexity)] -#![allow(clippy::missing_safety_doc)] #![cfg_attr(feature = "bench", feature(test))] #[cfg(feature = "alloc")] diff --git a/src/rb/local.rs b/src/rb/local.rs index cca93bc..ab29b57 100644 --- a/src/rb/local.rs +++ b/src/rb/local.rs @@ -34,6 +34,8 @@ impl End { } /// Ring buffer for single-threaded use only. +/// +/// Slightly faster than multi-threaded version because it doesn't synchronize cache. pub struct LocalRb { storage: Shared, read: End, @@ -46,7 +48,7 @@ impl LocalRb { /// # Safety /// /// The items in storage inside `read..write` range must be initialized, items outside this range must be uninitialized. - /// `read` and `write` positions must be valid (see [`RbBase`](`crate::ring_buffer::RbBase`)). + /// `read` and `write` positions must be valid (see implementation details). pub unsafe fn from_raw_parts(storage: S, read: usize, write: usize) -> Self { Self { storage: Shared::new(storage), diff --git a/src/rb/mod.rs b/src/rb/mod.rs index e5d77cd..b22818e 100644 --- a/src/rb/mod.rs +++ b/src/rb/mod.rs @@ -1,8 +1,11 @@ +/// Single-threaded ring buffer implementation. pub mod local; mod macros; +/// Multi-threaded ring buffer implementation. pub mod shared; -pub mod traits; +mod traits; mod utils; pub use local::LocalRb; pub use shared::SharedRb; +pub use traits::*; diff --git a/src/rb/shared.rs b/src/rb/shared.rs index 2d12d0d..e7f00fd 100644 --- a/src/rb/shared.rs +++ b/src/rb/shared.rs @@ -22,8 +22,8 @@ use crossbeam_utils::CachePadded; /// Ring buffer that can be shared between threads. /// -/// Note that there is no explicit requirement of `T: Send`. Instead [`Rb`] will work just fine even with `T: !Send` -/// until you try to send its [`Prod`] or [`Cons`] to another thread. +/// Note that there is no explicit requirement of `T: Send`. Instead ring buffer will work just fine even with `T: !Send` +/// until you try to send its producer or consumer to another thread. #[cfg_attr( feature = "std", doc = r##" @@ -58,7 +58,7 @@ impl SharedRb { /// # Safety /// /// The items in storage inside `read..write` range must be initialized, items outside this range must be uninitialized. - /// `read` and `write` positions must be valid (see [`RbBase`](`crate::ring_buffer::RbBase`)). + /// `read` and `write` positions must be valid (see implementation details). pub unsafe fn from_raw_parts(storage: S, read: usize, write: usize) -> Self { Self { storage: Shared::new(storage), diff --git a/src/rb/traits.rs b/src/rb/traits.rs index 8283fde..cddaff0 100644 --- a/src/rb/traits.rs +++ b/src/rb/traits.rs @@ -2,8 +2,15 @@ use crate::traits::RingBuffer; #[cfg(feature = "alloc")] use alloc::{rc::Rc, sync::Arc}; +/// Abstract pointer to the owning ring buffer. +/// +/// # Safety +/// +/// Implementation must be fair (e.g. not replacing pointers between calls and so on). pub unsafe trait RbRef: Clone + AsRef { + /// Underlying ring buffer. type Rb: RingBuffer; + /// Get ring buffer reference. fn rb(&self) -> &Self::Rb { self.as_ref() } diff --git a/src/storage.rs b/src/storage.rs index 4a3bb98..30924dc 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -6,10 +6,13 @@ use core::{cell::UnsafeCell, mem::MaybeUninit, num::NonZeroUsize, ops::Range, sl /// /// Storage items must be stored as a contiguous array. /// +/// Storage is converted to internal representation before use (see [`Self::Internal`]). +/// /// # Safety /// -/// *[`Self::len`]/[`Self::is_empty`] must always return the same value.* +/// *[`Self::len`] must always return the same value.* pub unsafe trait Storage { + /// Stored item. type Item: Sized; /// Internal representation of the storage. @@ -143,6 +146,8 @@ impl Shared { } } +/// Static storage. pub type Static = [MaybeUninit; N]; +/// Heap storage. #[cfg(feature = "alloc")] pub type Heap = Vec>; diff --git a/src/traits/consumer.rs b/src/traits/consumer.rs index 5c55b5d..f24b5a7 100644 --- a/src/traits/consumer.rs +++ b/src/traits/consumer.rs @@ -9,6 +9,13 @@ use std::io::{self, Write}; /// Consumer part of ring buffer. pub trait Consumer: Observer { + /// Set read index. + /// + /// # Safety + /// + /// Index must go only forward, never backward. It is recommended to use [`Self::advance_read_index`] instead. + /// + /// All slots with index less than `value` must be uninitialized until write index, all slots with index equal or greater - must be initialized. unsafe fn set_read_index(&self, value: usize); /// Moves `read` pointer by `count` places forward. @@ -33,7 +40,7 @@ pub trait Consumer: Observer { /// All items are initialized. Elements must be removed starting from the beginning of first slice. /// When all items are removed from the first slice then items must be removed from the beginning of the second slice. /// - /// *This method must be followed by [`Self::advance_read`] call with the number of items being removed previously as argument.* + /// *This method must be followed by [`Self::advance_read_index`] call with the number of items being removed previously as argument.* /// *No other mutating calls allowed before that.* fn occupied_slices(&self) -> (&[MaybeUninit], &[MaybeUninit]) { let (first, second) = unsafe { self.unsafe_slices(self.read_index(), self.write_index()) }; @@ -286,6 +293,7 @@ pub type Iter<'a, C: Consumer> = Chain, slice::Iter<'a, #[allow(type_alias_bounds)] pub type IterMut<'a, C: Consumer> = Chain, slice::IterMut<'a, C::Item>>; +/// Trait used for delegating producer methods. pub trait DelegateConsumer: DelegateObserver where Self::Base: Consumer, diff --git a/src/traits/mod.rs b/src/traits/mod.rs index e9af42d..e2edf51 100644 --- a/src/traits/mod.rs +++ b/src/traits/mod.rs @@ -1,6 +1,10 @@ +/// Consumer functionality. pub mod consumer; +/// Observer functionality. pub mod observer; +/// Producer functionality. pub mod producer; +/// Owning ring buffer functionality. pub mod ring_buffer; mod split; mod utils; diff --git a/src/traits/observer.rs b/src/traits/observer.rs index 75e7bd7..07e260b 100644 --- a/src/traits/observer.rs +++ b/src/traits/observer.rs @@ -1,6 +1,9 @@ use super::{utils::modulus, Based}; use core::{mem::MaybeUninit, num::NonZeroUsize}; +/// Ring buffer observer. +/// +/// Can observe ring buffer state but cannot safely access its data. pub trait Observer: Sized { type Item: Sized; @@ -9,9 +12,20 @@ pub trait Observer: Sized { /// It is constant during the whole ring buffer lifetime. fn capacity(&self) -> NonZeroUsize; + /// Index of the last item in the ring buffer. + /// + /// Index value is in range `0..(2 * capacity)`. fn read_index(&self) -> usize; + /// Index of the next empty slot in the ring buffer. + /// + /// Index value is in range `0..(2 * capacity)`. fn write_index(&self) -> usize; + /// Get mutable slice between `start` and `end` indices. + /// + /// # Safety + /// + /// There must not exist overlapping slices at the same time. unsafe fn unsafe_slices(&self, start: usize, end: usize) -> (&mut [MaybeUninit], &mut [MaybeUninit]); /// Whether read end is held by consumer. @@ -52,6 +66,7 @@ pub trait Observer: Sized { } } +/// Trait used for delegating observer methods. pub trait DelegateObserver: Based where Self::Base: Observer, diff --git a/src/traits/producer.rs b/src/traits/producer.rs index 4f62398..35372e6 100644 --- a/src/traits/producer.rs +++ b/src/traits/producer.rs @@ -14,6 +14,13 @@ use std::{ /// Producer part of ring buffer. pub trait Producer: Observer { + /// Set read index. + /// + /// # Safety + /// + /// Index must go only forward, never backward. It is recommended to use [`Self::advance_write_index`] instead. + /// + /// All slots with index less than `value` must be initialized until write index, all slots with index equal or greater - must be uninitialized. unsafe fn set_write_index(&self, value: usize); /// Moves `write` pointer by `count` places forward. @@ -40,8 +47,10 @@ pub trait Producer: Observer { /// Vacant memory is uninitialized. Initialized items must be put starting from the beginning of first slice. /// When first slice is fully filled then items must be put to the beginning of the second slice. /// - /// *This method must be followed by [`Self::advance_write`] call with the number of items being put previously as argument.* + /// *This method must be followed by [`Self::advance_write_index`] call with the number of items being put previously as argument.* /// *No other mutating calls allowed before that.* + /// + /// *Vacant slices must not be used to store any data because their contents aren't synchronized properly.* fn vacant_slices_mut(&mut self) -> (&mut [MaybeUninit], &mut [MaybeUninit]) { unsafe { self.unsafe_slices(self.write_index(), self.read_index() + self.capacity().get()) } } @@ -139,6 +148,7 @@ pub trait Producer: Observer { } } +/// Trait used for delegating consumer methods. pub trait DelegateProducer: DelegateObserver where Self::Base: Producer, diff --git a/src/traits/ring_buffer.rs b/src/traits/ring_buffer.rs index 4c66b2d..e00b32c 100644 --- a/src/traits/ring_buffer.rs +++ b/src/traits/ring_buffer.rs @@ -4,7 +4,7 @@ use super::{ Observer, }; -/// An abstract ring buffer. +/// An abstract ring buffer that exclusively owns its data. /// /// # Details /// @@ -17,7 +17,17 @@ use super::{ /// without using the space for an extra element in container. /// And obviously we cannot store more than `capacity` items in the buffer, so `write - read` modulo `2 * capacity` is not allowed to be greater than `capacity`. pub trait RingBuffer: Observer + Consumer + Producer { + /// Tell whether read end of the ring buffer is held by consumer or not. + /// + /// # Safety + /// + /// Must not be set to `false` while consumer exists. unsafe fn hold_read(&self, flag: bool); + /// Tell whether write end of the ring buffer is held by producer or not. + /// + /// # Safety + /// + /// Must not be set to `false` while producer exists. unsafe fn hold_write(&self, flag: bool); /// Pushes an item to the ring buffer overwriting the latest item if the buffer is full. @@ -57,6 +67,7 @@ pub trait RingBuffer: Observer + Consumer + Producer { } } +/// Trait used for delegating owning ring buffer methods. pub trait DelegateRingBuffer: DelegateProducer + DelegateConsumer where Self::Base: RingBuffer, diff --git a/src/traits/split.rs b/src/traits/split.rs index d648841..ea058a5 100644 --- a/src/traits/split.rs +++ b/src/traits/split.rs @@ -1,19 +1,27 @@ use crate::traits::{Consumer, Producer, RingBuffer}; +/// Split the ring buffer onto producer and consumer. pub trait Split: RingBuffer { + /// Producer type. type Prod: Producer; + /// Consumer type. type Cons: Consumer; + /// Perform splitting. fn split(self) -> (Self::Prod, Self::Cons); } +/// Split the ring buffer by reference onto producer and consumer. pub trait SplitRef: RingBuffer { + /// Ref producer type. type RefProd<'a>: Producer + 'a where Self: 'a; + /// Ref consumer type. type RefCons<'a>: Consumer + 'a where Self: 'a; + /// Perform splitting by reference. fn split_ref(&mut self) -> (Self::RefProd<'_>, Self::RefCons<'_>); } diff --git a/src/traits/utils.rs b/src/traits/utils.rs index 3302cfe..72a5208 100644 --- a/src/traits/utils.rs +++ b/src/traits/utils.rs @@ -1,9 +1,15 @@ use super::Observer; use core::num::NonZeroUsize; +/// Trait that should be implemented by ring buffer wrappers. +/// +/// Used for automatically delegating methods. pub trait Based { + /// Type the wrapper based on. type Base; + /// Reference to base. fn base(&self) -> &Self::Base; + /// Mutable reference to base. fn base_mut(&mut self) -> &mut Self::Base; } diff --git a/src/wrap/caching.rs b/src/wrap/caching.rs index 7c1aa40..9cf0a2e 100644 --- a/src/wrap/caching.rs +++ b/src/wrap/caching.rs @@ -1,6 +1,10 @@ +//! Caching implementation. +//! +//! Fetches changes from the ring buffer only when there is no more slots to perform requested operation. + use super::{direct::Obs, frozen::Frozen, traits::Wrap}; use crate::{ - rb::traits::RbRef, + rb::RbRef, traits::{ consumer::{impl_consumer_traits, Consumer}, producer::{impl_producer_traits, Producer}, @@ -9,12 +13,14 @@ use crate::{ }; use core::{mem::MaybeUninit, num::NonZeroUsize}; -/// Caching wrapper of ring buffer. +/// Caching wrapper of a ring buffer. pub struct Caching { frozen: Frozen, } +/// Caching producer implementation. pub type CachingProd = Caching; +/// Caching consumer implementation. pub type CachingCons = Caching; impl Caching { @@ -25,10 +31,12 @@ impl Caching { Self { frozen: Frozen::new(rb) } } + /// Get ring buffer observer. pub fn observe(&self) -> Obs { self.frozen.observe() } + /// Freeze current state. pub fn freeze(self) -> Frozen { self.frozen } diff --git a/src/wrap/direct.rs b/src/wrap/direct.rs index f408d6b..642d264 100644 --- a/src/wrap/direct.rs +++ b/src/wrap/direct.rs @@ -1,6 +1,10 @@ +//! Direct implementation. +//! +//! All changes are synchronized with the ring buffer immediately. + use super::{frozen::Frozen, traits::Wrap}; use crate::{ - rb::traits::RbRef, + rb::RbRef, traits::{ consumer::{impl_consumer_traits, Consumer}, producer::{impl_producer_traits, Producer}, @@ -13,15 +17,16 @@ use core::{ ptr, }; +/// Direct wrapper of a ring buffer. pub struct Direct { rb: R, } -/// Observer of ring buffer. +/// Observer of a ring buffer. pub type Obs = Direct; -/// Producer of ring buffer. +/// Producer of a ring buffer. pub type Prod = Direct; -/// Consumer of ring buffer. +/// Consumer of a ring buffer. pub type Cons = Direct; impl Clone for Obs { @@ -46,10 +51,12 @@ impl Direct { Self { rb } } + /// Get ring buffer observer. pub fn observe(&self) -> Obs { Obs { rb: self.rb.clone() } } + /// Freeze current state. pub fn freeze(self) -> Frozen { let this = ManuallyDrop::new(self); unsafe { Frozen::new_unchecked(ptr::read(&this.rb)) } diff --git a/src/wrap/frozen.rs b/src/wrap/frozen.rs index 850f94e..5fef037 100644 --- a/src/wrap/frozen.rs +++ b/src/wrap/frozen.rs @@ -1,6 +1,10 @@ +//! Frozen implementation. +//! +//! Changes are not synchronized with the ring buffer until its explicitly requested or when dropped. + use super::{direct::Obs, traits::Wrap}; use crate::{ - rb::traits::RbRef, + rb::RbRef, traits::{ consumer::{impl_consumer_traits, Consumer}, producer::{impl_producer_traits, Producer}, @@ -14,6 +18,7 @@ use core::{ ptr, }; +/// Frozen wrapper of the ring buffer. pub struct Frozen { rb: R, read: Cell, @@ -34,10 +39,6 @@ pub type FrozenCons = Frozen; impl Frozen { /// Create new ring buffer cache. - /// - /// # Safety - /// - /// There must be only one instance containing the same ring buffer reference. pub fn new(rb: R) -> Self { if P { assert!(!rb.rb().write_is_held()); @@ -50,6 +51,11 @@ impl Frozen { unsafe { Self::new_unchecked(rb) } } + /// Create wrapper without checking that such wrapper already exists. + /// + /// # Safety + /// + /// There must be maximum one instance of producer and one - of consumer. pub(crate) unsafe fn new_unchecked(rb: R) -> Self { Self { read: Cell::new(rb.rb().read_index()), @@ -58,6 +64,7 @@ impl Frozen { } } + /// Get ring buffer observer. pub fn observe(&self) -> Obs { Obs::new(self.rb.clone()) } diff --git a/src/wrap/mod.rs b/src/wrap/mod.rs index 46f69a2..4b8f572 100644 --- a/src/wrap/mod.rs +++ b/src/wrap/mod.rs @@ -1,8 +1,9 @@ pub mod caching; pub mod direct; pub mod frozen; -pub mod traits; +mod traits; pub use caching::{CachingCons, CachingProd}; pub use direct::{Cons, Obs, Prod}; pub use frozen::{FrozenCons, FrozenProd}; +pub use traits::*; diff --git a/src/wrap/traits.rs b/src/wrap/traits.rs index d201d99..aa12395 100644 --- a/src/wrap/traits.rs +++ b/src/wrap/traits.rs @@ -1,6 +1,6 @@ -use crate::rb::traits::RbRef; +use crate::rb::RbRef; -/// Ring buffer reference wrapper. +/// Ring buffer wrapper that contains reference to the ring buffer inside. pub trait Wrap: AsRef + AsMut { /// Ring buffer reference type. type RbRef: RbRef;