Async Internals
This page explains how BoltFFI bridges Rust futures to target language async systems. You don’t need to understand this to use async functions, but it helps when debugging or optimizing async code.
The Polling Model
BoltFFI wraps each Rust future in a RustFuture<T> that exposes a C-compatible interface. The bindings call poll with a continuation callback. If the future is pending, Rust stores that callback and returns; when the future wakes, Rust invokes the callback with MaybeReady, and bindings poll again. When the callback reports Ready, bindings exit the cycle, then run complete and free.
Who does what, and when
- Bindings pull: call
entryto create a handle. - Bindings pull: call
poll(handle, continuation)once. - Rust polls once:
- if ready, Rust immediately invokes continuation with
Ready; - if pending, Rust stores continuation and returns.
- if ready, Rust immediately invokes continuation with
- Rust pushes wake signal: when the underlying future wakes, Rust invokes continuation with
MaybeReady. - Bindings pull again: on that callback, bindings call
pollagain. - Repeat steps 3-5 until callback is
Ready. - Bindings finalize: call
complete, thenfree.
There is no busy polling loop in user code. Between polls, bindings wait for Rust to invoke the continuation.
Generated FFI Functions
For each async function, BoltFFI generates five FFI functions:
- entry - Creates the
RustFutureand returns a handle - poll - Polls the future with a continuation callback
- complete - Extracts the result once the future is ready
- cancel - Marks the future as cancelled
- free - Deallocates the future
The bindings use a callback handshake: entry creates the handle, poll registers/waits, callback returns MaybeReady or Ready, MaybeReady triggers another poll, and Ready ends polling before complete/free.
Continuation Callbacks
When bindings call poll, they pass a continuation callback plus callback data. If the future is pending, BoltFFI stores that continuation. When the future wakes (I/O completes, timer fires, etc.), BoltFFI invokes the stored callback with MaybeReady; bindings then poll again. A Ready callback means polling is finished and the call should finalize.
Lock-Free Implementation
Continuation scheduling uses atomic state tags and compare-and-swap transitions. Future execution state and result storage are still guarded by a mutex.
Cancellation
When the target language cancels an async operation, bindings call cancel. BoltFFI marks the future as cancelled and wakes any stored continuation with a Ready signal so waiting bindings can stop polling promptly. Cleanup then runs through free, and cancellation is surfaced by the target runtime’s wrapper.