Expand description
enso_channel
Bounded. Lock-free. Batch-native.
enso_channel is a batch-first concurrency primitive: a family of bounded, lock-free,
ring-buffer channels designed for bursty, latency-sensitive systems.
The API is intentionally non-blocking: operations are exposed as try_* and surface
backpressure/termination explicitly via errors (Full, Empty, Disconnected).
§Mental model
Instead of sending items one-by-one, producers typically:
- claim a contiguous range in the ring buffer,
- write into it,
- commit the range.
Receivers observe items via RAII guards/iterators; dropping them commits consumption.
§Misuse prevention (compile-time)
Batch receives return a guard that commits consumption on drop. To keep this sound,
the guard is intentionally not an Iterator<Item = &T>.
use enso_channel::mpsc;
let (mut tx, mut rx) = mpsc::channel::<u64>(4);
tx.try_send(1).unwrap();
let batch = rx.try_recv_many(1).unwrap();
// `RecvIter` is not an iterator; use `batch.iter()` instead.
for v in batch {
let _ = v;
}References yielded by batch.iter() are tied to the borrow of the batch guard and
cannot outlive it:
use enso_channel::mpsc;
let (mut tx, mut rx) = mpsc::channel::<u64>(4);
tx.try_send(1).unwrap();
let r: &u64 = {
let batch = rx.try_recv_many(1).unwrap();
batch.iter().next().unwrap()
};
let _ = *r;And you can’t commit (drop/finish) the guard while holding a reference from it:
use enso_channel::mpsc;
let (mut tx, mut rx) = mpsc::channel::<u64>(4);
tx.try_send(1).unwrap();
let batch = rx.try_recv_many(1).unwrap();
let first = batch.iter().next().unwrap();
batch.finish();
let _ = *first;§Public API modules
mpsc: multi-producer, single-consumerbroadcast: lossless fixed-N fanout (each receiver sees every item)mpmc: multi-producer, multi-consumer work distribution
§Non-goals
- no blocking API
- no async/await integration
- no dynamic resizing
- no built-in scheduling policy
§Lifecycle / shutdown (RAII)
This crate intentionally does not expose an explicit close()/terminate() API.
Shutdown is expressed through normal Rust endpoint lifecycle:
- dropping the last sender initiates shutdown; receivers may drain already-committed items
and then observe
Disconnected; - dropping the last receiver disconnects senders (subsequent sends return
Disconnected).
§Concurrency caveat
Disconnection is eventual, not transactional. In concurrent code, an operation may still succeed while the peer endpoint is being dropped, and already-committed items may never be observed by the application.