Expand description
AsyncSpscRing: Future-shaped async adapter on top of
crate::blocking_spsc_ring::BlockingSpscRing.
Turns the synchronous send_blocking / recv_blocking API into
send(...).await / recv(...).await so SubEtha rings compose
with any async executor (tokio, smol, async-std, custom). The
adapter is executor-agnostic: it uses only std::future::Future
plus std::thread to bridge the cross-process kernel-park to
the Rust Waker ecosystem.
§How the bridge works
Rust’s async model is “Future returns Pending and registers a
Waker; something fires the Waker; executor re-polls.” The
underlying CrossProcessWaker is a kernel-park primitive that
does the wait OFF the async runtime’s thread. To bridge:
- First poll calls
try_*on the inner ring. If immediately ready, returnPoll::Ready. - Otherwise, spawn a std::thread that calls the blocking
counterpart (
recv_blocking/send_blocking) with the caller-supplied timeout. Park the rust Waker. - When the blocking call returns, store the result + fire the Waker.
- Next poll observes the stored result and returns
Poll::Ready.
§Why a bounded timeout is required
Dropping a pending AsyncRecv / AsyncSend future does NOT
cancel the spawned worker thread (std::thread lacks safe
cancellation). The thread’s worst-case lifetime equals the
caller-supplied timeout. Unbounded waits are rejected at the
type level by requiring Duration (not Option<Duration>).
§Worker-thread cost model
Each in-flight future spawns one OS thread. This pattern fits
the substrate’s intended use of async (a small number of
long-running consumer tasks per process, not thousands of
short-lived futures). Callers driving high concurrency batch
through one BlockingSpscRing per consumer task and call
recv_blocking directly inside tokio::task::spawn_blocking
(or the executor’s equivalent).
Structs§
- Async
Recv - Future returned by
AsyncSpscRing::recv. - Async
Send - Future returned by
AsyncSpscRing::send. - Async
Spsc Ring - Wrapper providing
.recv(timeout).awaitand.send(timeout).await.