Skip to main content

ringbuf/
lib.rs

1//! Lock-free SPSC FIFO ring buffer with direct access to inner data.
2//!
3//! # Usage
4//!
5//! At first you need to create the ring buffer itself. [`HeapRb`] is recommended but you may [choose another one](#types).
6//!
7//! After the ring buffer is created it may be splitted into pair of [`Producer`](`traits::Producer`) and [`Consumer`](`traits::Consumer`).
8//! Producer is used to insert items to the ring buffer, consumer - to remove items from it.
9//!
10//! # Types
11//!
12//! There are several types of ring buffers provided:
13//!
14//! + [`LocalRb`]. Only for single-threaded use.
15//! + [`SharedRb`]. Can be shared between threads. Its frequently used instances:
16//!   + [`HeapRb`]. Contents are stored in dynamic memory. *Recommended for use in most cases.*
17//!   + [`StaticRb`]. Contents can be stored in statically-allocated memory.
18//!
19//! You may also provide your own generic parameters.
20//!
21//! # Performance
22//!
23//! [`SharedRb`] needs to synchronize CPU cache between CPU cores. This synchronization has some overhead.
24//! To avoid multiple unnecessary synchronizations you may use methods that operate many items at once
25//! ([`push_slice`](`traits::Producer::push_slice`)/[`push_iter`](`traits::Producer::push_iter`), [`pop_slice`](`traits::Consumer::pop_slice`), etc.).
26//! Caching endpoints also avoid repeatedly fetching the opposite endpoint's index when progress is possible.
27//! All completed operations publish their index updates immediately, including each step of [`pop_iter`](`traits::Consumer::pop_iter`).
28//! The former frozen endpoints are deprecated compatibility wrappers; see [`wrap::frozen`] for migration details.
29//!
30//! [`skip`](`traits::Consumer::skip`) and [`clear`](`traits::Consumer::clear`) take constant time for items without destructors.
31//! Items that need destruction are dropped individually, with each slot kept occupied until its destructor finishes.
32//!
33//! For single-threaded usage [`LocalRb`] is recommended because it is slightly faster than [`SharedRb`] due to absence of CPU cache synchronization.
34//!
35//! # Examples
36//!
37#![cfg_attr(
38    feature = "alloc",
39    doc = r##"
40## Simple
41
42```rust
43use ringbuf::{traits::*, HeapRb};
44
45# fn main() {
46let rb = HeapRb::<i32>::new(2);
47let (mut prod, mut cons) = rb.split();
48
49prod.try_push(0).unwrap();
50prod.try_push(1).unwrap();
51assert_eq!(prod.try_push(2), Err(2));
52
53assert_eq!(cons.try_pop(), Some(0));
54
55prod.try_push(2).unwrap();
56
57assert_eq!(cons.try_pop(), Some(1));
58assert_eq!(cons.try_pop(), Some(2));
59assert_eq!(cons.try_pop(), None);
60# }
61```
62"##
63)]
64#![doc = r##"
65## No heap
66
67```rust
68use ringbuf::{traits::*, StaticRb};
69
70# fn main() {
71const RB_SIZE: usize = 1;
72let mut rb = StaticRb::<i32, RB_SIZE>::default();
73let (mut prod, mut cons) = rb.split_ref();
74
75assert_eq!(prod.try_push(123), Ok(()));
76assert_eq!(prod.try_push(321), Err(321));
77
78assert_eq!(cons.try_pop(), Some(123));
79assert_eq!(cons.try_pop(), None);
80# }
81```
82"##]
83#![cfg_attr(
84    feature = "std",
85    doc = r##"
86## Overwrite
87
88Ring buffer can be used in overwriting mode when insertion overwrites the oldest element if the buffer is full.
89
90```rust
91use ringbuf::{traits::*, HeapRb};
92
93# fn main() {
94let mut rb = HeapRb::<i32>::new(2);
95
96assert_eq!(rb.push_overwrite(0), None);
97assert_eq!(rb.push_overwrite(1), None);
98assert_eq!(rb.push_overwrite(2), Some(0));
99
100assert_eq!(rb.try_pop(), Some(1));
101assert_eq!(rb.try_pop(), Some(2));
102assert_eq!(rb.try_pop(), None);
103# }
104```
105
106Note that [`push_overwrite`](`traits::RingBuffer::push_overwrite`) requires exclusive access to the ring buffer
107so to perform it concurrently you need to guard the ring buffer with mutex or some other lock.
108"##
109)]
110//!
111//! # Implementation details
112//!
113//! Each ring buffer here consists of the following parts:
114//!
115//! + Storage
116//! + Indices
117//! + Hold flags
118//!
119//! ## Storage
120//!
121//! [`Storage`](`storage::Storage`) is a place where ring buffer items are actually stored.
122//! It must span a single contiguous memory area (e.g. we can obtain a slice or subslice of it).
123//! Ring buffer can own its storage or it can hold only a mutable reference to it.
124//! Storage length is refered as `capacity`.
125//! Capacity must be in `1..=usize::MAX / 2`, including for zero-sized items.
126//!
127//! ## Indices
128//!
129//! Ring buffer also contains two indices: `read` and `write`.
130//!
131//! `read % capacity` points to the oldest item in the storage.
132//! `write % capacity` points to empty slot next to the most recently inserted item.
133//!
134//! When an item is extracted from the ring buffer it is taken from the `read % capacity` slot and then `read` index is incremented.
135//! New item is put into the `write % capacity` slot and `write` index is incremented after that.
136//!
137//! Slots with indices between (in modular arithmetic) `(read % capacity)` (including) and
138//! `(write % capacity)` (excluding) contain items (are initialized).
139//! All other slots do not contain items (are uninitialized).
140//!
141//! The actual values of `read` and `write` indices are modulo `2 * capacity` instead of just `capacity`.
142//! It allows us to distinguish situations when the buffer is empty (`read == write`)
143//! and when the buffer is full (`(write - read) % (2 * capacity) == capacity`)
144//! without using an extra slot in container that cannot be occupied.
145//!
146//! But this causes the existense of invalid combinations of indices.
147//! For example, we cannot store more than `capacity` items in the buffer,
148//! so `(write - read) % (2 * capacity)` is not allowed to be greater than `capacity`.
149//!
150//! ## Hold flags
151//!
152//! Ring buffer can have at most one producer and at most one consumer at the same time.
153//! These flags indicates whether it's safe to obtain a new producer or a consumer.
154//!
155#![no_std]
156#![allow(clippy::type_complexity)]
157#![cfg_attr(feature = "bench", feature(test))]
158
159#[cfg(feature = "alloc")]
160extern crate alloc;
161#[cfg(feature = "std")]
162extern crate std;
163
164/// Shortcuts for frequently used types.
165mod alias;
166/// Ring buffer implementations.
167pub mod rb;
168/// Storage types.
169pub mod storage;
170/// Ring buffer traits.
171pub mod traits;
172/// Items transfer between ring buffers.
173mod transfer;
174/// Internal utilities.
175mod utils;
176/// Producer and consumer implementations.
177pub mod wrap;
178
179#[cfg(test)]
180mod tests;
181
182pub use alias::*;
183pub use rb::{LocalRb, SharedRb};
184pub use traits::{consumer, producer};
185pub use transfer::transfer;
186pub use wrap::{CachingCons, CachingProd, Cons, Obs, Prod};
187
188#[cfg(feature = "bench")]
189extern crate test;
190#[cfg(feature = "bench")]
191mod benchmarks;