Expand description
Triple buffering in Rust
In this crate, we propose a Rust implementation of triple buffering. This is a non-blocking thread synchronization mechanism that can be used when a single producer thread is frequently updating a shared data block, and a single consumer thread wants to be able to read the latest available version of the shared data whenever it feels like it.
§Examples
For many use cases, you can use the ergonomic write/read interface, where the producer moves values into the buffer and the consumer accesses the latest buffer by shared reference:
// Create a triple buffer
use triple_buffer::triple_buffer;
let (mut buf_input, mut buf_output) = triple_buffer(&0);
// The producer thread can move a value into the buffer at any time
let producer = std::thread::spawn(move || buf_input.write(42));
// The consumer thread can read the latest value at any time
let consumer = std::thread::spawn(move || {
let latest = buf_output.read();
assert!(*latest == 42 || *latest == 0);
});
In situations where moving the original value away and being unable to modify it on the consumer’s side is too costly, such as if creating a new value involves dynamic memory allocation, you can use a lower-level API which allows you to access the producer and consumer’s buffers in place and to precisely control when updates are propagated:
// Create and split a triple buffer
use triple_buffer::triple_buffer;
let (mut buf_input, mut buf_output) = triple_buffer(&String::with_capacity(42));
// --- PRODUCER SIDE ---
// Mutate the input buffer in place
{
// Acquire a reference to the input buffer
let input = buf_input.input_buffer_mut();
// In general, you don't know what's inside of the buffer, so you should
// always reset the value before use (this is a type-specific process).
input.clear();
// Perform an in-place update
input.push_str("Hello, ");
}
// Publish the above input buffer update
buf_input.publish();
// --- CONSUMER SIDE ---
// Manually fetch the buffer update from the consumer interface
buf_output.update();
// Acquire read-only reference to the output buffer
let output = buf_output.peek_output_buffer();
assert_eq!(*output, "Hello, ");
// Or acquire mutable reference if necessary
let output_mut = buf_output.output_buffer_mut();
// Post-process the output value before use
output_mut.push_str("world!");
Finally, as a middle ground before the maximal ergonomics of the
write()
API and the maximal control of the
input_buffer_mut()
/publish()
API, you can also use the
input_buffer_publisher()
RAII API on the
producer side, which ensures that publish()
is automatically called when
the resulting input buffer handle goes out of scope:
// Create and split a triple buffer
use triple_buffer::triple_buffer;
let (mut buf_input, _) = triple_buffer(&String::with_capacity(42));
// Mutate the input buffer in place and publish it
{
// Acquire a reference to the input buffer
let mut input = buf_input.input_buffer_publisher();
// In general, you don't know what's inside of the buffer, so you should
// always reset the value before use (this is a type-specific process).
input.clear();
// Perform an in-place update
input.push_str("Hello world!");
// Input buffer is automatically published at the end of the scope of
// the "input" RAII guard
}
// From this point on, the consumer can see the updated version
Structs§
- Input
- Producer interface to the triple buffer
- Input
Publish Guard - RAII Guard to the buffer provided by an
Input
. - Output
- Consumer interface to the triple buffer
- Triple
Buffer - A triple buffer, useful for nonblocking and thread-safe data sharing
Functions§
- triple_
buffer - Shorthand for
TripleBuffer::new(initial).split()