Expand description
§OneRingBuf
One Ring to rule them all, One Ring to find them,
One Ring to bring them all and in the mem’ry bind them
In the Land of Rust where the Data lies.
A lock-free, single-producer, single-consumer (SPSC) ring buffer optimised for real-time applications. It offers in-place mutability, asynchronous support, and virtual memory optimisation.
§Key Features
- Lock-Free SPSC: Designed for high-throughput, low-latency communication between two threads.
- In-Place Mutability: Modify elements directly in the buffer without needing to move them, reducing copying and improving performance.
- Asynchronous Support: First-class
async/awaitintegration for non-blocking operations. - Virtual Memory Optimisation: Uses
vmemto map the buffer to contiguous virtual memory, allowing for safe and efficient access to slices of data. - Flexible Memory Allocation: Supports both stack and heap allocation to suit different needs.
§When to Use oneringbuf
This crate was specifically developed for real-time audio stream processing, but it is well-suited for any scenario that requires a high-performance SPSC queue, such as:
- Real-time signal processing
- Game development (e.g. for audio or event queues)
- High-frequency data streaming
- Anywhere you need to pass data between two threads with minimal overhead.
A practical example for audio processing can be found here.
§Getting Started
Add oneringbuf to your Cargo.toml:
[dependencies]
oneringbuf = "0.6.0" # Replace with the latest versionHere is a basic example of creating a buffer, producing, and consuming data:
use oneringbuf::LocalHeapRB;
fn main() {
// Create a heap-allocated ring buffer with a capacity for 1024 elements.
let buf = LocalHeapRB::from(vec![0; 1024]);
let (mut prod, mut cons) = buf.split();
// Push some data into the buffer.
for i in 1..=10 {
prod.push(i).unwrap();
}
// Consume the data from the buffer.
for _ in 0..10 {
if let Some(val) = cons.pop() {
println!("Popped: {}", val);
}
}
}§Choosing the Right Buffer
oneringbuf provides several buffer types to suit different use cases. Here’s a guide to help you choose:
- Stack-Allocated (
*StackRB): Ideal for small buffers where the size is known at compile time. Offers fast allocation but is limited by stack space. - Heap-Allocated (
*HeapRB): Suitable for large buffers or when the size is determined at runtime. More flexible but with a small overhead for dynamic memory management.
And for concurrency:
- Local (
Local*RB): For single-threaded use. Offers the best performance due to simpler synchronisation. - Shared (
Shared*RB): For multi-threaded environments where the producer and consumer are on different threads. Uses atomic operations for thread safety. - Async (
Async*RB): Designed forasync/await, providing non-blocking operations suitable for asynchronous runtimes.
§Core Concepts
§In-place Mutability
A key feature of oneringbuf is the ability to modify elements directly within the buffer. This is achieved through the WorkIter, which provides mutable references to items that have been produced but not yet consumed. This avoids unnecessary data copying and can significantly improve performance in scenarios like audio processing or data transformations.
use oneringbuf::{LocalHeapRBMut, ORBIterator};
let buf = LocalHeapRBMut::from(vec![0; 4096]);
let (mut prod, mut work, mut cons) = buf.split_mut();
// Produce some data
prod.push(10).unwrap();
// Mutate the data in-place
if let Some(item) = work.get_mut() {
*item *= 2;
unsafe { work.advance(1); }
}
// The consumer will now see the modified value
assert_eq!(cons.pop(), Some(20));§Advanced Features
§Virtual Memory (vmem) Optimisation
For circular buffers, a powerful optimisation is to map the underlying buffer to two contiguous regions of virtual memory. This allows you to treat the circular buffer as a linear one, making it possible to read or write data across the wrap-around point in a single operation. More information can be found here.
This crate supports this optimisation through the vmem feature flag, which is currently limited to unix targets. When using vmem, the buffer size (length of the buffer times the size of the stored type) must be a multiple of the system’s page size (usually 4096 for x86_64).
At the moment, the feature has been tested on GNU/Linux, Android, macOS and iOS.
§A Note About iOS
vmem works by allocating shared memory. While this doesn’t represent a problem on other platforms, it is different on iOS. Users should create an app group (more information here) and then set the environment variable IOS_APP_GROUP_NAME to the name of that group.
§Building and Running Examples
To run the tests, benchmarks, or examples, clone the repository and use the following commands from the root directory.
To run tests:
cargo +nightly test --testsTo run benchmarks:
cargo benchTo run the CPAL example:
RUSTFLAGS="--cfg cpal" cargo run --example cpalIf you encounter an error like ALSA lib pcm_dsnoop.c:567:(snd_pcm_dsnoop_open) unable to open slave, please refer to this issue.
To run any other example (e.g., simple_async):
cargo run --example simple_async --features async§Best Practices
- Handle Uninitialised Items with Care: If you create a buffer with uninitialised memory (e.g., with
new_zeroed), you must use*_initmethods (e.g.,push_init) to safely write data. Using normal methods on uninitialised memory can lead to Undefined Behaviour. - Match Buffer Type to Use Case: Choose the buffer type that best fits your performance and concurrency needs.
- Buffer Sizing for
vmem: When using thevmemfeature, ensure your buffer size is a multiple of the system’s page size to prevent panics. - Graceful Shutdown: The buffer is deallocated only when the last of its iterators is dropped. Ensure all iterators are dropped for proper cleanup.
§License
This work is dual-licensed under:
- Apache License, Version 2.0 LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0
- MIT license LICENSE-MIT or https://opensource.org/license/MIT
You can choose between one of them.
Modules§
- iterators
- Module containing sync and async iterators.
- iters_
components - Components used by the buffers to manage iterators.
- storage_
components - Components used by the buffers to store data.
- utils
- Various utilities
Structs§
- OneRing
Buf - The One Ring aka the main struct of this crate. All the other buffers are based upon this.
Traits§
- IntoRef
- Trait used to create a shared reference out of a buffer.
- Iter
Component - Trait implemented by all iterator components.
- ORBIterator
- Trait implemented by iterators.
- OneRB
- Trait implemented by ring buffers.
- Storage
Component - Trait implemented by storage components.
Type Aliases§
- Async
HeapRB allocandasync - Non-mutable heap-allocated asynchronous ring buffer.
- Async
HeapRB Mut allocandasync - Mutable heap-allocated asynchronous ring buffer.
- Async
StackRB async - Non-mutable stack-allocated asynchronous ring buffer.
- Async
StackRB Mut async - Mutable stack-allocated asynchronous ring buffer.
- Async
VmemRB allocandasyncandvmemand Unix - Non-mutable asynchronous ring buffer using virtual memory storage.
- Async
VmemRB Mut allocandasyncandvmemand Unix - Mutable asynchronous ring buffer using virtual memory storage.
- Local
HeapRB alloc - Non-mutable heap-allocated ring buffer suitable for single-threaded usage.
- Local
HeapRB Mut alloc - Mutable heap-allocated ring buffer suitable for single-threaded usage.
- Local
StackRB - Non-mutable stack-allocated ring buffer suitable for single-threaded usage.
- Local
StackRB Mut - Mutable stack-allocated ring buffer suitable for single-threaded usage.
- Local
VmemRB allocandvmemand Unix - Non-mutable ring buffer using virtual memory storage suitable for single-threaded usage.
- Local
VmemRB Mut allocandvmemand Unix - Mutable ring buffer using virtual memory storage suitable for single-threaded usage.
- Shared
HeapRB alloc - Non-mutable heap-allocated ring buffer suitable for multi-threaded usage.
- Shared
HeapRB Mut alloc - Mutable heap-allocated ring buffer suitable for multi-threaded usage.
- Shared
StackRB - Non-mutable stack-allocated ring buffer suitable for multi-threaded usage.
- Shared
StackRB Mut - Mutable stack-allocated ring buffer suitable for multi-threaded usage.
- Shared
VmemRB allocandvmemand Unix - Non-mutable ring buffer using virtual memory storage suitable for multi-threaded usage.
- Shared
VmemRB Mut allocandvmemand Unix - Mutable ring buffer using virtual memory storage suitable for multi-threaded usage.