Expand description
kevy-ring — a lock-free, bounded single-producer / single-consumer ring.
One producer pushes, one consumer pops, with no locks and no per-message allocation: a fixed power-of-two slot array plus two monotonic cursors. It is the cross-core transport primitive for kevy-rt’s shared-nothing, thread-per-core runtime (the Seastar/Scylla model) — each ordered pair of cores gets its own ring, so a hot reactor never contends a lock on the hop.
The single-producer / single-consumer contract is enforced by the type
system: push and pop take &mut self,
and Producer/Consumer are distinct owned halves, so the compiler
guarantees at most one thread pushes and one pops. That keeps the ordering
requirements minimal — a single Release/Acquire pair per operation.
Pure Rust, zero dependencies. The lock-free buffer needs UnsafeCell +
atomics, so this crate is not #![forbid(unsafe_code)]; every unsafe block
documents the SPSC invariant it relies on (no C, no FFI — see the kevy
pure-Rust principle). Part of the kevy key–value server.
§Example
let (mut tx, mut rx) = kevy_ring::ring::<u32>(4);
assert!(tx.push(1).is_ok());
assert!(tx.push(2).is_ok());
assert_eq!(rx.pop(), Some(1));
assert_eq!(rx.pop(), Some(2));
assert_eq!(rx.pop(), None);Producer and consumer move to different threads:
let (mut tx, mut rx) = kevy_ring::ring::<u64>(1024);
let prod = std::thread::spawn(move || {
for i in 0..10_000u64 {
while tx.push(i).is_err() {
std::hint::spin_loop(); // ring full — let the consumer drain
}
}
});
let mut next = 0u64;
while next < 10_000 {
if let Some(v) = rx.pop() {
assert_eq!(v, next); // FIFO, nothing lost or reordered
next += 1;
}
}
prod.join().unwrap();Structs§
- Consumer
- The receiving half.
Send(move to the consumer thread); only this half pops. - Producer
- The sending half.
Send(move to the producer thread); only this half pushes.
Functions§
- ring
- Create a ring holding at least
capacityitems (rounded up to a power of two, minimum 2), returning its producer and consumer halves.