Crates.io | ringbuf |
lib.rs | ringbuf |
version | 0.4.7 |
source | src |
created_at | 2019-01-15 08:56:13.279794 |
updated_at | 2024-09-27 14:20:16.454531 |
description | Lock-free SPSC FIFO ring buffer with direct access to inner data |
homepage | |
repository | https://github.com/agerasev/ringbuf.git |
max_upload_size | |
id | 108657 |
size | 150,330 |
Lock-free SPSC FIFO ring buffer with direct access to inner data.
Copy
).Read
and Write
implementation.std
and even without alloc
(using only statically-allocated memory).portable-atomic
crate to allow usage on smaller systems without CAS operations.At first you need to create the ring buffer itself. HeapRb
is recommended but you may choose another one.
After the ring buffer is created it may be splitted into pair of Producer
and Consumer
.
Producer is used to insert items to the ring buffer, consumer - to remove items from it.
There are several types of ring buffers provided:
LocalRb
. Only for single-threaded use.SharedRb
. Can be shared between threads. Its frequently used instances:
HeapRb
. Contents are stored in dynamic memory. Recommended for use in most cases.StaticRb
. Contents can be stored in statically-allocated memory.You may also provide your own generic parameters.
SharedRb
needs to synchronize CPU cache between CPU cores. This synchronization has some overhead.
To avoid multiple unnecessary synchronizations you may use methods that operate many items at once
(push_slice
/push_iter
, pop_slice
/pop_iter
, etc.)
or you can freeze
producer or consumer and then synchronize threads manually (see items in frozen
module).
For single-threaded usage LocalRb
is recommended because it is slightly faster than SharedRb
due to absence of CPU cache synchronization.
use ringbuf::{traits::*, HeapRb};
let rb = HeapRb::<i32>::new(2);
let (mut prod, mut cons) = rb.split();
prod.try_push(0).unwrap();
prod.try_push(1).unwrap();
assert_eq!(prod.try_push(2), Err(2));
assert_eq!(cons.try_pop(), Some(0));
prod.try_push(2).unwrap();
assert_eq!(cons.try_pop(), Some(1));
assert_eq!(cons.try_pop(), Some(2));
assert_eq!(cons.try_pop(), None);
use ringbuf::{traits::*, StaticRb};
const RB_SIZE: usize = 1;
let mut rb = StaticRb::<i32, RB_SIZE>::default();
let (mut prod, mut cons) = rb.split_ref();
assert_eq!(prod.try_push(123), Ok(()));
assert_eq!(prod.try_push(321), Err(321));
assert_eq!(cons.try_pop(), Some(123));
assert_eq!(cons.try_pop(), None);
Ring buffer can be used in overwriting mode when insertion overwrites the latest element if the buffer is full.
use ringbuf::{traits::*, HeapRb};
let mut rb = HeapRb::<i32>::new(2);
assert_eq!(rb.push_overwrite(0), None);
assert_eq!(rb.push_overwrite(1), None);
assert_eq!(rb.push_overwrite(2), Some(0));
assert_eq!(rb.try_pop(), Some(1));
assert_eq!(rb.try_pop(), Some(2));
assert_eq!(rb.try_pop(), None);
Note that push_overwrite
requires exclusive access to the ring buffer
so to perform it concurrently you need to guard the ring buffer with mutex or some other lock.
Licensed under either of
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.