executor
import std::future::executor; · source
Types
Executor
type struct Executor {
inner: ExecInner*;
worker_tids: [u64];
static new() -> Executor;
static with_threads(n: u32) -> Executor;
static with_threads_and_blocking(n: u32, blocking: u32) -> Executor;
spawn<F, O>(mut &this, fut: F) -> JoinHandle<O>
where F: Future<O>;
spawn_detached<F, O>(mut &this, fut: F) -> void
where F: Future<O>;
block_on<F, O>(mut &this, root: F) -> O
where F: Future<O>;
}
An Executor owns a queue of ready tasks and a pool of worker threads that drive them, plus the reactor and blocking pool those tasks park on.
import std::future;
mut ex: Executor = Executor::new();
const result: Report = ex.block_on(run_pipeline(config));
new sizes the worker pool to the hardware thread count; with_threads picks the count yourself and with_threads_and_blocking also caps the blocking pool — the threads that run getaddrinfo, file I/O, and child waits off the executor. spawn returns a JoinHandle and the workers begin driving the future immediately; spawn_detached is fire and forget; block_on spawns the root and blocks the calling thread until it finishes.
join and block_on never poll on the calling thread — the workers own all polling.
Properties
| Property | Type |
|---|---|
inner | ExecInner* |
worker_tids | [u64] |
Constructors
static new() -> Executor;
static new() -> Executor;Create an executor whose pool is sized to the hardware parallelism.
static with_threads(n: u32) -> Executor;
static with_threads(n: u32) -> Executor;Create an executor with exactly n worker threads (clamped to >= 1 so the pool always makes progress) and the default blocking-pool ceiling.
static with_threads_and_blocking(n: u32, blocking: u32) -> Executor;
static with_threads_and_blocking(n: u32, blocking: u32) -> Executor;Create an executor with n worker threads and a blocking pool that will grow to at most blocking threads.
Methods
spawn<F, O>(mut &this, fut: F) -> JoinHandle<O> where F: Future<O>;
spawn<F, O>(mut &this, fut: F) -> JoinHandle<O> where F: Future<O>;Spawn fut and return a handle to its Output. Workers begin driving it immediately.
spawn_detached<F, O>(mut &this, fut: F) -> void where F: Future<O>;
spawn_detached<F, O>(mut &this, fut: F) -> void where F: Future<O>;Spawn fut as fire-and-forget: it runs to completion and its result is discarded. Sugar for spawn(fut).detach().
block_on<F, O>(mut &this, root: F) -> O where F: Future<O>;
block_on<F, O>(mut &this, root: F) -> O where F: Future<O>;Spawn root and block the calling thread until a worker drives it to completion, then return its value. Panics if root was cancelled or could not complete; use spawn(root).join() for the Result.
JoinHandle<O>
type struct JoinHandle<O> {
: TaskShared<O>*;
exec: ExecInner*;
join(mut this) -> Result<O, JoinError>;
detach(mut this) -> void;
abort(&this) -> void;
is_finished(&this) -> boolean;
}
Dropping a handle without joining or detaching detaches it: the task runs on and its result is discarded. abort() requests cancellation, and the task is reclaimed — its output never produced — the next time a worker reaches it; a join on an aborted task returns Err(Cancelled).
Properties
| Property | Type |
|---|---|
shared | TaskShared<O>* |
exec | ExecInner* |
Methods
join(mut this) -> Result<O, JoinError>;
join(mut this) -> Result<O, JoinError>;Block on the completion condvar until a worker finishes this task, then return its Output (or Err if it was aborted / could not complete / panicked). Consumes the handle and frees the block. Waiting on the handshake state (not merely outcome) guarantees the finishing worker is past its last touch of the block before we read+free it.
detach(mut this) -> void;
detach(mut this) -> void;Give up the ability to join. The task runs to completion independently; its result is dropped. Consumes the handle.
abort(&this) -> void;
abort(&this) -> void;Request that the task be cancelled. It is reclaimed — its Output never produced — the next time a worker reaches it (a subsequent join then returns Err(Cancelled)). Does not consume the handle.
is_finished(&this) -> boolean;
is_finished(&this) -> boolean;True once the task has finished (completed, cancelled, or panicked). Never blocks and does not consume the handle.
JoinError
type enum JoinError {
Cancelled;
Panicked(PanicInfo);
}
Cancelled is what join returns on an aborted task; Panicked carries the panic's message and location, and only occurs under --panic=unwind.
Dropping the Executor signals shutdown, joins the workers, then reclaims any still-queued tasks.
Variants
| Variant |
|---|
Cancelled |
Panicked(PanicInfo) |
Concurrent-poll safety
With two or more workers a task can be woken — by itself mid-poll, or later by another thread — while a worker is already polling it. Polling one future from two workers at once is a data race, so each task carries an atomic run state (IDLE, SCHEDULED, RUNNING, NOTIFIED) that guarantees a task is enqueued at most once and polled by at most one worker.
wake() moves IDLE → SCHEDULED and enqueues, or RUNNING → NOTIFIED and defers to the running worker. A worker moves SCHEDULED → RUNNING to poll, then RUNNING → IDLE on a quiet Pending, or re-schedules on NOTIFIED.
Task isolation
Under --panic=unwind the poll boundary runs inside a catch: a panicking task is caught, finishes as panicked, and its join returns Err(Panicked(info)) — its siblings keep running.
Under the default --panic=abort the catch machinery is a compile error, so the boundary is a plain direct call and a task panic aborts the whole process. That is the accepted degradation; see catch_unwind.
Trait implementations
Executor
implement trait Drop for struct Executor
JoinHandle
implement<O> trait Drop for struct JoinHandle<O>
Constants
const OUTCOME_PENDING: u8 = 0;
const OUTCOME_READY: u8 = 1;
const OUTCOME_CANCELLED: u8 = 2;
const OUTCOME_PANICKED: u8 = 3;
const HS_LIVE: u8 = 0;
const HS_TASK_FIN: u8 = 1;
const HS_HANDLE_GONE: u8 = 2;
const ST_IDLE: u8 = 0;
const ST_SCHEDULED: u8 = 1;
const ST_RUNNING: u8 = 2;
const ST_NOTIFIED: u8 = 3;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 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.
Executor.inner: ExecInner*type struct ExecInner {
q_mutex: [u8;
40];
q_cond: [u8;
48];
d_mutex: [u8;
40];
d_cond: [u8;
48];
head: Task*;
tail: Task*;
// … 3 more
}The shared executor state: a mutex+condvar-guarded FIFO of ready tasks plus a second mutex+condvar joiners block on. Heap-pinned so a Task's exec back-pointer and the worker threads reference a stable address (the pthread buffers must not move after init while threads block on them).
Executor.worker_tids: [u64]static 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 Executor::new() -> ExecutorCreate an executor whose pool is sized to the hardware parallelism.
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.
static Executor::with_threads(n: u32) -> ExecutorCreate an executor with exactly n worker threads (clamped to >= 1 so the pool always makes progress) and the default blocking-pool ceiling.
(parameter) n: u32static Executor::with_threads_and_blocking(n: u32, blocking: u32) -> ExecutorCreate an executor with n worker threads and a blocking pool that will grow to at most blocking threads.
(parameter) blocking: u32Executor::spawn<F, O>(mut &this, fut: F) -> JoinHandle<O>
where F: Future<O>Spawn fut and return a handle to its Output. Workers begin driving it immediately.
type parameter Fwhere F: Future
type parameter Omut &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) fut: Ftype 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 parameter Owhere T: Ord + Clone, U: HashConstrains generic parameters to types that implement the listed traits, which is what makes those traits' methods callable on T. Bounds on one parameter join with +; parameters separate with ,. A where on a single method narrows the type's parameter for that method only.
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).
Executor::spawn_detached<F, O>(mut &this, fut: F) -> void
where F: Future<O>Spawn fut as fire-and-forget: it runs to completion and its result is discarded. Sugar for spawn(fut).detach().
type parameter OExecutor::block_on<F, O>(mut &this, root: F) -> O
where F: Future<O>Spawn root and block the calling thread until a worker drives it to completion, then return its value. Panics if root was cancelled or could not complete; use spawn(root).join() for the Result.
(parameter) root: Ftype parameter Oimport 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.
mut name: Type = value;A mutable binding, reassignable after declaration - mutability is opt-in, const is the default. At module level it declares mutable global state. Before & it marks an exclusive reference or receiver instead.
mut ex: Executorstatic Executor::new() -> ExecutorCreate an executor whose pool is sized to the hardware parallelism.
std::future::executorconst 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 result: Reportmut ex: ExecutorExecutor::block_on<F, O>(mut &this, root: F) -> O
where F: Future<O>Spawn root and block the calling thread until a worker drives it to completion, then return its value. Panics if root was cancelled or could not complete; use spawn(root).join() for the Result.
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.
JoinHandle.shared: TaskShared<O>*type struct TaskShared<O> {
ctl: TaskCtl;
result: O;
}The control block shared between a task and its JoinHandle<O>. Outlives the task so join can read the result after the task finishes. result is uninitialized until the task stores it on the READY path; the handshake guarantees it is read/dropped exactly once and never before the store.
type parameter OJoinHandle.exec: ExecInner*JoinHandle::join(mut this) -> Result<O, JoinError>Block on the completion condvar until a worker finishes this task, then return its Output (or Err if it was aborted / could not complete / panicked). Consumes the handle and frees the block.
mut thisA by-value receiver the body may also reassign. The receiver is moved into the method and consumed; the caller's value may not be used afterwards.
language referencetype enum JoinError {
Cancelled;
Panicked(PanicInfo);
}Why a join did not yield a value.
JoinHandle::detach(mut this) -> voidGive up the ability to join. The task runs to completion independently; its result is dropped. Consumes the handle.
JoinHandle::abort(&this) -> voidRequest that the task be cancelled. It is reclaimed — its Output never produced — the next time a worker reaches it (a subsequent join then returns Err(Cancelled)). Does not consume the handle.
JoinHandle::is_finished(&this) -> booleanTrue once the task has finished (completed, cancelled, or panicked). Never blocks and does not consume the handle.
type enum Name {
Unit,
Payload(T),
Named { x: i32 },
}An algebraic data type. Variants may be bare or carry a tuple or struct payload, and a match on an enum must cover every variant. Name::Variant names a variant; methods are added with implement enum Name { ... }.
type enum JoinError {
Cancelled;
Panicked(PanicInfo);
}Why a join did not yield a value.
JoinError::CancelledJoinError::Panicked(PanicInfo)type struct PanicInfo {
msg: string;
file: string;
line: u32;
}The payload a caught panic reports: the panic message and the source location it was raised from. Mirrors the fields the runtime funnel (runtime/panic/unwind) stashes when it raises.
const OUTCOME_PENDING: u8 = 0;const OUTCOME_READY: u8 = 1;const OUTCOME_CANCELLED: u8 = 2;const OUTCOME_PANICKED: u8 = 3;const HS_LIVE: u8 = 0;const HS_TASK_FIN: u8 = 1;const HS_HANDLE_GONE: u8 = 2;const ST_IDLE: u8 = 0;const ST_SCHEDULED: u8 = 1;const ST_RUNNING: u8 = 2;const ST_NOTIFIED: u8 = 3;namespace std::future::executor;std::future::executor: a multi-threaded, poll-driven task executor.
module pagenew T { field: value }
new E::Variant(args)
new T[n]Heap allocation. new T { .. } places a struct literal on the heap and yields a T*, and new E::V(args) does the same for an enum variant; new T[n] reserves n uninitialised T - the typed malloc. The storage comes from T's Allocate implementation, or else the global allocator. Parentheses after a struct's name are refused (E0117): there are no constructors, and a struct's static new(..) is an ordinary method.
static Executor::with_threads(n: u32) -> ExecutorCreate an executor with exactly n worker threads (clamped to >= 1 so the pool always makes progress) and the default blocking-pool ceiling.
static Executor::with_threads_and_blocking(n: u32, blocking: u32) -> ExecutorCreate an executor with n worker threads and a blocking pool that will grow to at most blocking threads.
Executor::spawn<F, O>(mut &this, fut: F) -> JoinHandle<O>
where F: Future<O>Spawn fut and return a handle to its Output. Workers begin driving it immediately.
Executor::spawn_detached<F, O>(mut &this, fut: F) -> void
where F: Future<O>Spawn fut as fire-and-forget: it runs to completion and its result is discarded. Sugar for spawn(fut).detach().
JoinHandle::join(mut this) -> Result<O, JoinError>Block on the completion condvar until a worker finishes this task, then return its Output (or Err if it was aborted / could not complete / panicked). Consumes the handle and frees the block.
Executor.inner: ExecInner*Executor.worker_tids: [u64]static Task::new(poll_fn: (u8*, u8*, Context*) -> u8, drop_fn: (u8*) -> void, dispose_fn: (u8*, u8) -> void, fut: u8*, shared: u8*, exec: ExecInner*) -> TaskBuild a ready-to-enqueue task around a boxed future + its shared control block. run_state starts SCHEDULED (the caller enqueues immediately) and next is unlinked; the vtable fn-ptrs are the future's monomorphized thunks.
ExecInner.blocking: BlockingPool*Task.fut: u8*type struct Output {
status: ExitStatus;
stdout: Array<u8>;
stderr: Array<u8>;
}JoinHandle::detach(mut this) -> voidGive up the ability to join. The task runs to completion independently; its result is dropped. Consumes the handle.
std::future::executorJoinHandle::abort(&this) -> voidRequest that the task be cancelled. It is reclaimed — its Output never produced — the next time a worker reaches it (a subsequent join then returns Err(Cancelled)). Does not consume the handle.
Result::Err(E)JoinError::CancelledTask.shared: u8*Task.exec: ExecInner*TaskCtl.outcome: Atomic<u8>JoinHandle::is_finished(&this) -> booleanTrue once the task has finished (completed, cancelled, or panicked). Never blocks and does not consume the handle.
std::future::executorJoinError::Panicked(PanicInfo)Poll::Pendingimplement struct Name { ... }
implement trait Trait for Type { ... }
implement u64 { ... }Adds methods to a type outside its declaration. implement struct T (or enum, or a bare primitive) opens an inherent block; implement trait Tr for T supplies a trait's methods for one type. Resolution is at compile time - no dynamic dispatch.
implement trait Trait for Type { ... }Introduces a trait implementation: the block supplies, for one concrete type, the method bodies the trait requires.
language referenceimplement trait Trait for Type { ... }Names the type a trait implementation is for. The block that follows supplies the bodies the trait requires.
language referencetype trait Name : Base {
method(&this, other: &This) -> Ret;
}Names a set of methods a type may implement. Inside the body This is the implementing type; methods may carry default bodies, and a trait may require a base trait (type trait Ord : Eq). Generic code asks for a trait with where T: Name.