4 releases
0.2.1 | Oct 22, 2024 |
---|---|
0.2.0 | Jun 3, 2024 |
0.1.1 | May 30, 2024 |
0.1.0 | May 30, 2024 |
#375 in Concurrency
Used in 2 crates
24KB
187 lines
Direct Ring Buffer
This crate provides a high-performance, lock-free ring buffer for single-producer, single-consumer scenarios, where efficient writing and reading of elements is crucial.
Overview
A ring buffer is a fixed-size buffer that operates as a circular queue. This implementation uses a lock-free approach, making it suitable for real-time applications where minimal latency is important. The buffer supports efficient bulk operations through block-based reads and writes, making it ideal for scenarios with one producer and one consumer.
Features
- Single-Producer, Single-Consumer: Designed for scenarios with a single writer and a single reader.
- Lock-Free: Utilizes atomic operations for synchronization, avoiding the need for mutexes or other locking mechanisms.
- Slice-Based I/O: Supports reading and writing multiple elements at a time using slices, enhancing performance for bulk operations.
- Closure-Based Access: Provides direct access to the buffer through closures, allowing for flexible and efficient element processing.
- Single Element Read/Write: Supports not only slice-based operations but also single element read and write operations.
Type Constraints
The buffer requires the type T
to implement the Copy
trait because it operates on uninitialized memory. The usage of Copy
depends on the operation:
- Slice-based operations like
read_slices
andwrite_slices
do not requireCopy
, allowing direct memory access without copying elements. - Single-element operations like
read_element
andwrite_element
requireCopy
to safely handle the copying of individual elements.
Example
use direct_ring_buffer::{create_ring_buffer, Producer, Consumer};
let (mut producer, mut consumer) = create_ring_buffer::<u8>(5);
// Write data to the buffer in slices
producer.write_slices(|data, _offset| {
data[..3].copy_from_slice(&[1, 2, 3]);
3
}, None);
producer.write_slices(|data, _offset| {
data[..1].copy_from_slice(&[4]);
1
}, None);
// Read the data
consumer.read_slices(|data, _offset| {
assert_eq!(&data[..4], &[1, 2, 3, 4]);
4
}, None);
// Test wrap-around by writing more data
producer.write_slices(|data, offset| {
if offset == 0 {
data[..1].copy_from_slice(&[6]);
1
} else if offset == 1 {
data[..1].copy_from_slice(&[7]);
1
} else {
panic!("Unexpected offset: {}", offset);
}
}, None);
// Verify the wrap-around data
consumer.read_slices(|data, offset| {
if offset == 0 {
assert_eq!(data, &[6]); // Read the last part of the buffer
} else if offset == 1 {
assert_eq!(data, &[7]); // Read the wrapped-around part
} else {
panic!("Unexpected offset: {}", offset); // Ensure only 0 or 1 is valid
}
data.len()
}, None);
// Write 5 more values to test wrap-around in one go
let test_data = [8, 9, 10, 11, 12];
producer.write_slices(|data, offset| {
let write_len = data.len().min(test_data.len() - offset);
data[..write_len].copy_from_slice(&test_data[offset..offset + write_len]);
write_len
}, None);
// Read the newly written values to verify wrap-around
consumer.read_slices(|data, offset| {
let read_len = data.len().min(test_data.len() - offset);
assert_eq!(&data[..read_len], &test_data[offset..offset + read_len]);
read_len
}, None);
Note: For single element operations using read_element or write_element, see the documentation in lib.rs.
Safety and Performance
This implementation uses unsafe blocks with proper checks to ensure safe access to uninitialized memory. Atomic operations are utilized for synchronization, minimizing overhead and allowing for high-performance scenarios where low-latency and real-time constraints are critical.
Optimized for Bulk Operations
This ring buffer is optimized for reading and writing multiple elements at once, reducing overhead for batch processing. While it supports single-element operations, bulk operations maximize performance and minimize overhead.
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT) at your option.
Contribution
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.