reactor
import std::future::reactor; · source
Types
Reactor
type struct Reactor {
mtx: [u8;
40];
buckets: [Registration*;
64];
poll_h: i64;
kick_h: i64;
aux_h: i64;
shutdown: Atomic<u8>;
thread: u64;
timers: Timer*;
timer_seq: u64;
static start() -> Reactor*;
static stop(rt: Reactor*) -> void;
static free(rt: Reactor*) -> void;
static current() -> Reactor*;
static set_current(rt: Reactor*) -> void;
static require() -> Reactor*;
register(mut &this, fd: i32, direction: u32, w: Waker) -> void;
cancel(mut &this, fd: i32, direction: u32) -> void;
deregister(mut &this, fd: i32) -> void;
poll_once(mut &this, timeout_ms: i32) -> void;
run_once(mut &this) -> void;
register_timer(mut &this, deadline: i64, w: Waker) -> u64;
cancel_timer(mut &this, id: u64) -> void;
}
The I/O readiness driver behind every socket future: epoll on Linux, an AFD/IOCP wait on Windows. An Executor starts one and its workers set it as the thread's ambient reactor; you touch it only when writing a new leaf future.
A future whose syscall reports "would block" calls register(fd, direction, waker) with the waker from its Context and returns Pending. The reactor's own thread sits in the OS readiness call until the descriptor is ready, then fires the stored waker, which re-schedules the task; that second poll retries the syscall, which now succeeds.
Two properties of register are correctness requirements, not tuning:
- One-shot. A delivered event disarms that direction. A future that still cannot finish simply registers again. This is the native shape of a Windows AFD poll and is requested explicitly (
EPOLLONESHOT) on Linux. - Level-triggered. A descriptor that is already ready at arm time is reported immediately. A future attempts its syscall before it registers, so readiness can arrive in the window between the two; level-triggered arming closes that window, where edge-triggered would park the future forever.
Spurious wakes are always safe: a woken future retries its syscall, and if that still would block it registers again.
cancel(fd, direction) releases one direction's registration without closing anything — what a future's Drop calls — and deregister(fd) removes the descriptor entirely. Timers share the same thread: register_timer(deadline, waker) returns an id that cancel_timer disarms, and the reactor bounds its readiness wait by the earliest deadline, which is how Sleep costs no thread. current() is the thread's ambient reactor; require() panics if there is none, which is the error you see when a socket future is polled outside an executor.
Properties
| Property | Type |
|---|---|
mtx | [u8 |
40] | |
buckets | [Registration* |
64] | |
poll_h | i64 |
kick_h | i64 |
aux_h | i64 |
shutdown | Atomic<u8> |
thread | u64 |
timers | Timer* |
timer_seq | u64 |
Constructors
static start() -> Reactor*;
static start() -> Reactor*;Create a reactor, open its OS backend, and start its I/O thread. Panics if the OS refuses any of the three: an executor without a working reactor would park I/O futures that could never be woken, so failing loudly at construction beats hanging later.
static current() -> Reactor*;
static current() -> Reactor*;The reactor driving the current thread's polls, or null if this thread is not an executor worker.
static require() -> Reactor*;
static require() -> Reactor*;The current thread's reactor, or a panic explaining that async I/O needs one. Called by a future at the moment it is about to park: returning Pending with nowhere to register a waker would hang the task forever, so failing loudly here beats failing silently later.
Static functions
static stop(rt: Reactor*) -> void;
static stop(rt: Reactor*) -> void;Stop the reactor and release everything it owns, leaving the block itself allocated. Signals shutdown, kicks the thread out of its wait, joins it, then drops every registration and timer still standing — releasing their wakers, which is the last reference to any task still parked on I/O or a deadline, so those tasks are reclaimed rather than leaked.
static free(rt: Reactor*) -> void;
static free(rt: Reactor*) -> void;Release the reactor block. Must run only after every future that could still cancel against this reactor has been dropped — see stop.
static set_current(rt: Reactor*) -> void;
static set_current(rt: Reactor*) -> void;Publish rt as this thread's reactor. Called by an executor worker as it starts, so every future it polls can reach the reactor.
Methods
register(mut &this, fd: i32, direction: u32, w: Waker) -> void;
register(mut &this, fd: i32, direction: u32, w: Waker) -> void;Arm fd for one wake in direction (exactly one INTEREST_* bit) and store w as the waker to fire. Replaces any waker already stored for that direction (a re-registering future supersedes its own earlier one).
cancel(mut &this, fd: i32, direction: u32) -> void;
cancel(mut &this, fd: i32, direction: u32) -> void;Disarm one direction on fd, releasing the waker stored for it. Called by an I/O future that is dropped while parked, so cancelling one half of a socket leaves a future waiting on the other half untouched.
deregister(mut &this, fd: i32) -> void;
deregister(mut &this, fd: i32) -> void;Drop fd's registration entirely, releasing every waker it held. Called when the descriptor itself goes away — a recycled descriptor number must never inherit a stale registration.
poll_once(mut &this, timeout_ms: i32) -> void;
poll_once(mut &this, timeout_ms: i32) -> void;Wait for readiness (up to timeout_ms; negative blocks indefinitely) and fire the wakers of every descriptor that became ready. The reactor thread calls this in a loop.
run_once(mut &this) -> void;
run_once(mut &this) -> void;One iteration of the reactor thread: wait for readiness, but no longer than the earliest armed timer allows, dispatch whatever became ready, then fire every timer that came due. Keeping the wait bound here leaves poll_once a pure "wait at most this long" primitive.
register_timer(mut &this, deadline: i64, w: Waker) -> u64;
register_timer(mut &this, deadline: i64, w: Waker) -> u64;Arm a one-shot timer firing w at deadline (monotonic nanoseconds, the scale Instant::as_nanos reports) and return the id that disarms it.
cancel_timer(mut &this, id: u64) -> void;
cancel_timer(mut &this, id: u64) -> void;Disarm the timer with id, releasing the waker it held. A no-op when the timer has already fired or was never armed, which is what lets a dropped Sleep cancel unconditionally without tracking whether it fired.
Constants
const INTEREST_READ: u32 = 1;
const INTEREST_WRITE: u32 = 2;
const REG_BUCKETS: i32 = 64;
const RT_MAX_EVENTS: i32 = 64;type struct Name { ... }
type enum Name { ... }
type trait Name { ... }
type union Name { ... }Begins a type declaration; the keyword that follows says which kind. On its own, type Name = Existing; declares an alias.
type struct Name {
field: Type;
method(&this) -> Ret { ... }
}A value type with named fields and optional methods. Structs live on the stack and are passed by value. Fields are public by default and may carry = default values; methods take their receiver as &this, mut &this, or this.
type 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.
Reactor.mtx: [u8Reactor.buckets: [Registration*type struct Registration {
fd: i32;
interest: u32;
read_waker: Waker;
write_waker: Waker;
next: Registration*;
backend_added: boolean;
os_state: u8*;
}One descriptor's slot in the reactor's table: which directions are armed, and the waker to fire for each. Heap-allocated and owned by the reactor (never by a future — futures move, and a backend may hand the OS a pointer into this block that must stay put until the operation completes).
std::future::reactorReactor.poll_h: i64Reactor.kick_h: i64Reactor.aux_h: i64Reactor.shutdown: Atomic<u8>type struct Atomic<T> {
value: T;
}A single atomic cell of type T. See the module docs for the supported T and the compile-time dispatch model.
Reactor.thread: u64Reactor.timers: Timer*type struct Timer {
id: u64;
deadline: i64;
waker: Waker;
next: Timer*;
}One armed timer: fire waker once the monotonic clock reaches deadline. Heap-allocated and owned by the reactor for the same reason a Registration is — a future moves, so it holds only the id, never a pointer to this block.
Reactor.timer_seq: u64static name(param: Type) -> Ret { ... }A method that belongs to the type rather than an instance, called as Type::name(..). For structs, static new(..) returning a struct literal is the idiomatic constructor.
static Reactor::start() -> Reactor*Create a reactor, open its OS backend, and start its I/O thread. Panics if the OS refuses any of the three: an executor without a working reactor would park I/O futures that could never be woken, so failing loudly at construction beats hanging later.
type 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.
static Reactor::stop(rt: Reactor*) -> voidStop the reactor and release everything it owns, leaving the block itself allocated.
(parameter) rt: Reactor*static Reactor::free(rt: Reactor*) -> voidRelease the reactor block. Must run only after every future that could still cancel against this reactor has been dropped — see stop.
static Reactor::current() -> Reactor*The reactor driving the current thread's polls, or null if this thread is not an executor worker.
static Reactor::set_current(rt: Reactor*) -> voidPublish rt as this thread's reactor. Called by an executor worker as it starts, so every future it polls can reach the reactor.
static Reactor::require() -> Reactor*The current thread's reactor, or a panic explaining that async I/O needs one. Called by a future at the moment it is about to park: returning Pending with nowhere to register a waker would hang the task forever, so failing loudly here beats failing silently later.
Reactor::register(mut &this, fd: i32, direction: u32, w: Waker) -> voidArm fd for one wake in direction (exactly one INTEREST_* bit) and store w as the waker to fire. Replaces any waker already stored for that direction (a re-registering future supersedes its own earlier one).
mut &this
mut &TypeAn exclusive, mutating borrow. As a receiver, mut &this lets the method modify the fields - and callers can see that from the signature alone. As a parameter type, mut &T lets the callee mutate through the reference while the caller keeps ownership.
&this
mut &this
thisThe receiver of a method. Its first parameter declares how the method takes it: &this borrows read-only, mut &this borrows exclusively, and this / mut this move the value in and consume it. In the body, this.field reaches the members.
(parameter) fd: i32(parameter) direction: u32(parameter) w: Wakertype 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.
Reactor::cancel(mut &this, fd: i32, direction: u32) -> voidDisarm one direction on fd, releasing the waker stored for it. Called by an I/O future that is dropped while parked, so cancelling one half of a socket leaves a future waiting on the other half untouched.
Reactor::deregister(mut &this, fd: i32) -> voidDrop fd's registration entirely, releasing every waker it held. Called when the descriptor itself goes away — a recycled descriptor number must never inherit a stale registration.
Reactor::poll_once(mut &this, timeout_ms: i32) -> voidWait for readiness (up to timeout_ms; negative blocks indefinitely) and fire the wakers of every descriptor that became ready. The reactor thread calls this in a loop.
(parameter) timeout_ms: i32Reactor::run_once(mut &this) -> voidOne iteration of the reactor thread: wait for readiness, but no longer than the earliest armed timer allows, dispatch whatever became ready, then fire every timer that came due. Keeping the wait bound here leaves poll_once a pure "wait at most this long" primitive.
Reactor::register_timer(mut &this, deadline: i64, w: Waker) -> u64Arm a one-shot timer firing w at deadline (monotonic nanoseconds, the scale Instant::as_nanos reports) and return the id that disarms it.
(parameter) deadline: i64Reactor::cancel_timer(mut &this, id: u64) -> voidDisarm the timer with id, releasing the waker it held. A no-op when the timer has already fired or was never armed, which is what lets a dropped Sleep cancel unconditionally without tracking whether it fired.
(parameter) id: u64const NAME: Type = value;At module level, a true compile-time constant, conventionally SCREAMING_SNAKE_CASE. (mut at module level declares mutable global state instead.)
const INTEREST_READ: u32 = 1;const INTEREST_WRITE: u32 = 2;const REG_BUCKETS: i32 = 64;const RT_MAX_EVENTS: i32 = 64;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).
namespace std::future;std::future: stackless async foundations — Future, Poll, Context, Waker, and the drivers that poll them.
namespace std::future::reactor;std::future::reactor: the I/O readiness driver behind async I/O.
module pagetype 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.
Reactor::register(mut &this, fd: i32, direction: u32, w: Waker) -> voidArm fd for one wake in direction (exactly one INTEREST_* bit) and store w as the waker to fire. Replaces any waker already stored for that direction (a re-registering future supersedes its own earlier one).
Registration.fd: i32Timer.waker: Wakertype 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.
Poll::PendingReactor::cancel(mut &this, fd: i32, direction: u32) -> voidDisarm one direction on fd, releasing the waker stored for it. Called by an I/O future that is dropped while parked, so cancelling one half of a socket leaves a future waiting on the other half untouched.
Reactor::deregister(mut &this, fd: i32) -> voidDrop fd's registration entirely, releasing every waker it held. Called when the descriptor itself goes away — a recycled descriptor number must never inherit a stale registration.
Reactor::register_timer(mut &this, deadline: i64, w: Waker) -> u64Arm a one-shot timer firing w at deadline (monotonic nanoseconds, the scale Instant::as_nanos reports) and return the id that disarms it.
Timer.deadline: i64Reactor::cancel_timer(mut &this, id: u64) -> voidDisarm the timer with id, releasing the waker it held. A no-op when the timer has already fired or was never armed, which is what lets a dropped Sleep cancel unconditionally without tracking whether it fired.
static Reactor::current() -> Reactor*The reactor driving the current thread's polls, or null if this thread is not an executor worker.
std::future::reactorstatic Reactor::require() -> Reactor*The current thread's reactor, or a panic explaining that async I/O needs one. Called by a future at the moment it is about to park: returning Pending with nowhere to register a waker would hang the task forever, so failing loudly here beats failing silently later.
Reactor.mtx: [u8Reactor.buckets: [Registration*Reactor.poll_h: i64Reactor.kick_h: i64Reactor.aux_h: i64Reactor.shutdown: Atomic<u8>Reactor.thread: u64Reactor.timers: Timer*Reactor.timer_seq: u64static Reactor::start() -> Reactor*Create a reactor, open its OS backend, and start its I/O thread. Panics if the OS refuses any of the three: an executor without a working reactor would park I/O futures that could never be woken, so failing loudly at construction beats hanging later.
std::future::reactorstatic Reactor::stop(rt: Reactor*) -> voidStop the reactor and release everything it owns, leaving the block itself allocated.
std::future::reactorstatic Reactor::free(rt: Reactor*) -> voidRelease the reactor block. Must run only after every future that could still cancel against this reactor has been dropped — see stop.
static Reactor::set_current(rt: Reactor*) -> voidPublish rt as this thread's reactor. Called by an executor worker as it starts, so every future it polls can reach the reactor.
Reactor::poll_once(mut &this, timeout_ms: i32) -> voidWait for readiness (up to timeout_ms; negative blocks indefinitely) and fire the wakers of every descriptor that became ready. The reactor thread calls this in a loop.
Reactor::run_once(mut &this) -> voidOne iteration of the reactor thread: wait for readiness, but no longer than the earliest armed timer allows, dispatch whatever became ready, then fire every timer that came due. Keeping the wait bound here leaves poll_once a pure "wait at most this long" primitive.
type struct Instant {
secs: i64;
nanos: i64;
}A measurement of the monotonic clock. Opaque: the fields are the raw timespec, meaningful only relative to another Instant. POD.
Instant::as_nanos(&this) -> i64This reading as raw monotonic nanoseconds. The absolute value is meaningful only relative to another Instant - it counts from an unspecified epoch (typically system boot) - so use it for lightweight elapsed-time math where the difference of two readings is all that matters and carrying a Duration would be overkill.
Timer.id: u64type struct Sleep {
deadline: i64;
id: u64;
rt: Reactor*;
}A future that completes once the monotonic clock reaches its deadline.
std::future::timer