future
The runtime half of Cryo's async support. async and await are a compile-time transformation — an async function becomes a state machine, and calling it builds a struct rather than running the body. Nothing happens until something polls it, and this module is what does the polling.
public module future::poll;
public module future::waker;
public module future::traits;
public module future::ready;
public module future::reactor;
public module future::blocking;
public module future::executor;
public module future::timer;
public module future::combinator;
The module is named future because async is a reserved keyword. See Asynchronous Programming for the language side. A plain import std::future; brings in the whole module.
| File | What it holds | Import |
|---|---|---|
traits | Future | import std::future::traits; |
poll | Poll<T> | import std::future::poll; |
waker | Context, Waker | import std::future::waker; |
ready | Ready<T>, PendingThenReady<T> — futures for tests | import std::future::ready; |
executor | Executor, JoinHandle, JoinError | import std::future::executor; |
combinator | Futures::join / try_join / select / timeout | import std::future::combinator; |
timer | Sleep | import std::future::timer; |
reactor | Reactor, the I/O readiness driver | import std::future::reactor; |
blocking | BlockingPool, BlockingJob — off-executor work | import std::future::blocking; |
How the pieces fit
A Future is polled with a Context, which carries a Waker. Returning Pending means "not yet — I have arranged to be woken"; returning Ready(v) means the value is available and the future is done. The executor owns the tasks and the worker threads that poll them; a parked task is woken by whichever of the two drivers it registered with — the reactor for descriptor readiness and timers, or the blocking pool for a call the OS cannot report readiness on. Everything in net, and the async halves of process and io, is a leaf future built on those two.
Cancellation has no mechanism of its own: dropping a parked future runs its Drop, which is where a Sleep disarms its timer and an I/O future releases its registration. See cancellation is a drop.
public function name() { ... }
public:Visible to any module that imports this one - the default for top-level items and for struct fields, so it is mostly written to be explicit. public: opens a visibility block that groups the members after it.
public module collections::array;In a directory's _module.cryo aggregator, lists a submodule and whether it is public. Importing the directory's namespace sees only the modules declared public here.
namespace std::future;std::future: stackless async foundations — Future, Poll, Context, Waker, and the drivers that poll them.
namespace std::future::ready;Hand-written sample futures: Ready<T> and PendingThenReady<T>.
namespace std::future::reactor;std::future::reactor: the I/O readiness driver behind async I/O.
module pagenamespace std::future::blocking;std::future::blocking: an off-executor home for work that cannot be polled.
module pagenamespace std::future::executor;std::future::executor: a multi-threaded, poll-driven task executor.
module pagenamespace std::future::timer;std::future::timer: Sleep, the future that completes when a deadline arrives.
namespace std::future::combinator;std::future::combinator: running futures against each other — Join, TryJoin, Select, and Timeout.
async function name(param: Type) -> T { ... }Compiles the function into a stackless state machine. Calling it builds a future whose output is the declared return type; nothing runs until that future is polled. Allowed on free functions, methods, and trait methods.
language referenceawait futureSuspends the enclosing async body until the operand - which must implement Future - completes, and evaluates to its output. A prefix operator allowed anywhere an expression is (conditions, arms, guards), but only inside async code.
function name<T>(param: Type) -> Ret { ... }Declares a function. Parameters are written name: Type and are never inferred; the return type follows -> and defaults to void. Declaration order does not matter: every signature is collected before any body is checked, so functions may call each other freely and recurse.
import a::b;
import a::b::{ X, Y };
import a::b as c;
import a::b::*;Brings a module, or selected items from it, into scope. One path per declaration: use the brace form for several items, as to alias, and * for everything public (prefer the brace form; wildcards invite name collisions).
type trait Future {
type Output;
poll(mut &this, cx: Context*) -> Poll<This::Output>;
}An asynchronous computation. poll drives it one step: Ready(v) when the value is available, Pending when it is not yet (the future has arranged, via cx's waker, to be polled again later).
type enum Poll<T> {
Ready(T);
Pending;
}The result of polling a Future: either the final value, or a signal that the value is not ready yet and the task should be re-polled after its Waker fires.
type struct Context {
waker: Waker;
}The context threaded through a poll call. Holds the Waker a future registers when it returns Pending. Passed by pointer down the poll tree, matching the stdlib convention of moving aggregates by pointer.
type struct Waker {
data: u8*;
}A handle a stalled future stores so it can be re-scheduled: wake_fn(data) tells the executor "poll my task again". This is the manual-vtable shape used throughout the runtime — non-capturing function pointers plus an opaque data pointer identifying the task.
type struct Ready<T> {
value: Option<T>;
}A future that is already complete: the first poll returns Ready(value). The value is held in an Option and moved out on completion, so polling a Ready a second time is a contract violation (it panics) rather than a double-move.
type struct PendingThenReady<T> {
remaining: u32;
value: Option<T>;
}A future that reports Pending for its first pending_count polls and then completes with value. Exercises the suspend/resume path end-to-end under a driver without any I/O or threads — the canonical Phase-1 validation future.
type struct Executor {
inner: ExecInner*;
worker_tids: [u64];
}A multi-threaded, poll-driven task executor. Construct one, spawn futures onto it (keeping the JoinHandle to await a result, or dropping it to fire-and-forget), and block_on to drive a root future to its value. A pool of worker threads does all polling; the calling thread only ever blocks on a completion condvar.
type struct JoinHandle<O> {
shared: TaskShared<O>*;
exec: ExecInner*;
}A handle to a spawned task, yielding its Output via join(). Dropping a handle without join/detach detaches (the task runs on, its result discarded). abort() requests cancellation.
type enum JoinError {
Cancelled;
Panicked(PanicInfo);
}Why a join did not yield a value.
static Futures::join<A, B, OA, OB>(a: A, b: B) -> Join<A, B, OA, OB>
where A: Future<OA>, B: Future<OB>Run a and b concurrently; complete with both values.
type struct Sleep {
deadline: i64;
id: u64;
rt: Reactor*;
}A future that completes once the monotonic clock reaches its deadline.
std::future::timertype struct Reactor {
mtx: [u8;
40];
buckets: [Registration*;
64];
poll_h: i64;
kick_h: i64;
aux_h: i64;
shutdown: Atomic<u8>;
thread: u64;
timers: Timer*;
// … 1 more
}The I/O readiness driver for one Executor. Heap-allocated: worker threads and parked futures hold raw pointers to it, and the backend hands the OS interior addresses, so it must not move.
type struct BlockingPool {
mtx: [u8;
40];
cv: [u8;
48];
head: BlockingJob*;
tail: BlockingJob*;
shutdown: boolean;
live: u32;
idle: u32;
max: u32;
// … 1 more
}A pool of OS threads running blocking jobs for one Executor. Heap-allocated: worker threads and submitted jobs hold raw pointers to it, so it must not move.
type struct BlockingJob {
rc: Atomic<u32>;
status: Atomic<u8>;
waker: Waker;
next: BlockingJob*;
pool: BlockingPool*;
}The header of a blocking job: the erased vtable, the waker to fire when the work finishes, and the refcount that decides who frees the block.
std::future::blockingPoll::Pending