Skip to content

Commit

Permalink
Add some items documentation to ringbuf, slightly reorganize modules
Browse files Browse the repository at this point in the history
  • Loading branch information
agerasev committed Dec 11, 2023
1 parent 0b64c76 commit 12937ee
Show file tree
Hide file tree
Showing 26 changed files with 136 additions and 32 deletions.
2 changes: 1 addition & 1 deletion async/src/rb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion async/src/wrap/cons.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use ringbuf::{
consumer::{Consumer, DelegateConsumer},
Observer,
},
wrap::traits::Wrap,
wrap::Wrap,
};
#[cfg(feature = "std")]
use std::io;
Expand Down
2 changes: 1 addition & 1 deletion async/src/wrap/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand Down
2 changes: 1 addition & 1 deletion async/src/wrap/prod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use ringbuf::{
producer::{DelegateProducer, Producer},
Observer,
},
wrap::traits::Wrap,
wrap::Wrap,
};
#[cfg(feature = "std")]
use std::io;
Expand Down
2 changes: 1 addition & 1 deletion blocking/src/rb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion blocking/src/wrap/cons.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion blocking/src/wrap/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand Down
2 changes: 1 addition & 1 deletion blocking/src/wrap/prod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 3 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -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")]
Expand Down
4 changes: 3 additions & 1 deletion src/rb/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<S: Storage> {
storage: Shared<S>,
read: End,
Expand All @@ -46,7 +48,7 @@ impl<S: Storage> LocalRb<S> {
/// # 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),
Expand Down
5 changes: 4 additions & 1 deletion src/rb/mod.rs
Original file line number Diff line number Diff line change
@@ -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::*;
6 changes: 3 additions & 3 deletions src/rb/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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##"
Expand Down Expand Up @@ -58,7 +58,7 @@ impl<S: Storage> SharedRb<S> {
/// # 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),
Expand Down
7 changes: 7 additions & 0 deletions src/rb/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self::Rb> {
/// Underlying ring buffer.
type Rb: RingBuffer;
/// Get ring buffer reference.
fn rb(&self) -> &Self::Rb {
self.as_ref()
}
Expand Down
7 changes: 6 additions & 1 deletion src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -143,6 +146,8 @@ impl<S: Storage> Shared<S> {
}
}

/// Static storage.
pub type Static<T, const N: usize> = [MaybeUninit<T>; N];
/// Heap storage.
#[cfg(feature = "alloc")]
pub type Heap<T> = Vec<MaybeUninit<T>>;
10 changes: 9 additions & 1 deletion src/traits/consumer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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<Self::Item>], &[MaybeUninit<Self::Item>]) {
let (first, second) = unsafe { self.unsafe_slices(self.read_index(), self.write_index()) };
Expand Down Expand Up @@ -286,6 +293,7 @@ pub type Iter<'a, C: Consumer> = Chain<slice::Iter<'a, C::Item>, slice::Iter<'a,
#[allow(type_alias_bounds)]
pub type IterMut<'a, C: Consumer> = Chain<slice::IterMut<'a, C::Item>, slice::IterMut<'a, C::Item>>;

/// Trait used for delegating producer methods.
pub trait DelegateConsumer: DelegateObserver
where
Self::Base: Consumer,
Expand Down
4 changes: 4 additions & 0 deletions src/traits/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
15 changes: 15 additions & 0 deletions src/traits/observer.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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<Self::Item>], &mut [MaybeUninit<Self::Item>]);

/// Whether read end is held by consumer.
Expand Down Expand Up @@ -52,6 +66,7 @@ pub trait Observer: Sized {
}
}

/// Trait used for delegating observer methods.
pub trait DelegateObserver: Based
where
Self::Base: Observer,
Expand Down
12 changes: 11 additions & 1 deletion src/traits/producer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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<Self::Item>], &mut [MaybeUninit<Self::Item>]) {
unsafe { self.unsafe_slices(self.write_index(), self.read_index() + self.capacity().get()) }
}
Expand Down Expand Up @@ -139,6 +148,7 @@ pub trait Producer: Observer {
}
}

/// Trait used for delegating consumer methods.
pub trait DelegateProducer: DelegateObserver
where
Self::Base: Producer,
Expand Down
13 changes: 12 additions & 1 deletion src/traits/ring_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use super::{
Observer,
};

/// An abstract ring buffer.
/// An abstract ring buffer that exclusively owns its data.
///
/// # Details
///
Expand All @@ -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.
Expand Down Expand Up @@ -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,
Expand Down
8 changes: 8 additions & 0 deletions src/traits/split.rs
Original file line number Diff line number Diff line change
@@ -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<'_>);
}
6 changes: 6 additions & 0 deletions src/traits/utils.rs
Original file line number Diff line number Diff line change
@@ -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;
}

Expand Down
Loading

0 comments on commit 12937ee

Please sign in to comment.