pub struct Scope<'r, P: Policy = FailFast> { /* private fields */ }Expand description
A scope for spawning work within a region.
The scope provides methods for:
- Spawning tasks
- Creating child regions
- Registering finalizers
- Cancelling all children
Implementations§
Source§impl<P: Policy> Scope<'_, P>
impl<P: Policy> Scope<'_, P>
Sourcepub fn spawn_actor<A: Actor>(
&self,
state: &mut RuntimeState,
cx: &Cx,
actor: A,
mailbox_capacity: usize,
) -> Result<(ActorHandle<A>, StoredTask), SpawnError>
pub fn spawn_actor<A: Actor>( &self, state: &mut RuntimeState, cx: &Cx, actor: A, mailbox_capacity: usize, ) -> Result<(ActorHandle<A>, StoredTask), SpawnError>
Spawns a new actor in this scope with the given mailbox capacity.
The actor runs as a region-owned task. Messages are delivered through a bounded MPSC channel with two-phase send semantics.
§Arguments
state- Runtime state for task creationcx- Capability contextactor- The actor instancemailbox_capacity- Bounded mailbox size
§Returns
A tuple of (ActorHandle, StoredTask). The StoredTask must be
registered with the runtime via state.store_spawned_task().
Sourcepub fn spawn_supervised_actor<A, F>(
&self,
state: &mut RuntimeState,
cx: &Cx,
factory: F,
strategy: SupervisionStrategy,
mailbox_capacity: usize,
) -> Result<(ActorHandle<A>, StoredTask), SpawnError>
pub fn spawn_supervised_actor<A, F>( &self, state: &mut RuntimeState, cx: &Cx, factory: F, strategy: SupervisionStrategy, mailbox_capacity: usize, ) -> Result<(ActorHandle<A>, StoredTask), SpawnError>
Spawns a supervised actor with explicit supervision semantics.
Unlike spawn_actor, this method takes a factory closure that can
produce new actor instances for restarts. The mailbox persists across
restarts, so messages sent while a restartable failure is being handled
are buffered for the next instance.
Because Actor has no explicit error return channel, supervised
crashes are treated as restartable failures when the strategy is
crate::supervision::SupervisionStrategy::Restart. If supervision
ultimately stops or escalates, the original panic payload is still
surfaced as JoinError::Panicked.
§Arguments
state- Runtime state for task creationcx- Capability contextfactory- Closure that creates actor instances (called on each restart)strategy- Supervision strategy (Stop, Restart, Escalate)mailbox_capacity- Bounded mailbox size
Source§impl<'scope, P: Policy> Scope<'scope, P>
impl<'scope, P: Policy> Scope<'scope, P>
Sourcepub fn capability_budget(&self) -> CapabilityBudget
pub fn capability_budget(&self) -> CapabilityBudget
Returns the capability/resource budget for this scope.
Sourcepub async fn map_reduce<I, M, F, T, E, R>(
&self,
cx: &Cx,
limits: MapReduceLimits,
inputs: I,
map: M,
reduce: R,
) -> MapReduceExecution<T, E>
pub async fn map_reduce<I, M, F, T, E, R>( &self, cx: &Cx, limits: MapReduceLimits, inputs: I, map: M, reduce: R, ) -> MapReduceExecution<T, E>
Execute bounded map tasks in this scope and reduce values in input order.
The concurrency limit bounds active maps. The separate retained-work
limit also includes completed values waiting for an earlier input, so
out-of-order completion cannot create an unbounded result backlog.
Reduction is a left fold and does not require a commutative reducer or
cloning mapped values. Empty input produces a successful None.
A terminal result is returned only after every admitted child finishes, including cancellation cleanup. Dropping the execution requests child cancellation; the owning region still enforces their eventual drain.
Sourcepub fn spawn_registered<F, Fut, Caps>(
&self,
state: &mut RuntimeState,
cx: &Cx<Caps>,
f: F,
) -> Result<TaskHandle<Fut::Output>, SpawnError>
pub fn spawn_registered<F, Fut, Caps>( &self, state: &mut RuntimeState, cx: &Cx<Caps>, f: F, ) -> Result<TaskHandle<Fut::Output>, SpawnError>
Spawns a task and registers it synchronously with the runtime state.
This legacy state-threaded path combines spawn() with
RuntimeState::store_spawned_task() and returns a canonical task id
immediately. Keep it for synchronous boot/test paths that must inspect
the task record before scheduler admission. Runtime-wired call sites
that do not require inline start-failure observation should use
Cx::spawn_registered_in, which registers through the v2 spawn
gateway without passing &mut RuntimeState.
§Arguments
state- The runtime state (for immediate task storage)cx- The capability context (for creating child context)f- A closure that produces the future, receiving the new task’sCx
§Returns
A TaskHandle<T> for awaiting the task’s result.
§Cancellation result
This legacy API preserves its established behavior: any value actually
returned by user code wins over a cancellation request, including when
the first poll begins after cancellation or the future never calls a
cancellation checkpoint. New runtime-wired code should prefer
Cx::spawn_registered_in, whose pre-first-poll cancellation remains
an outer task cancellation while preserving a returned value only after
user code acknowledges cancellation.
§Example
let handle = scope.spawn_registered(&mut state, &cx, |cx| async move {
cx.trace("Child task running");
compute_value().await
})?;
let result = handle.join(&cx).await?;Sourcepub async fn region<P2, F, Fut, T, Caps>(
&self,
state: &mut RuntimeState,
cx: &Cx<Caps>,
policy: P2,
f: F,
) -> Result<Outcome<T, P2::Error>, RegionCreateError>
pub async fn region<P2, F, Fut, T, Caps>( &self, state: &mut RuntimeState, cx: &Cx<Caps>, policy: P2, f: F, ) -> Result<Outcome<T, P2::Error>, RegionCreateError>
Creates a child region and runs the provided future within a child scope.
The child region inherits the parent’s budget by default. Use
Scope::region_with_budget to tighten constraints for the child.
The returned outcome is the result of the body future. After the body completes, the child region begins its close sequence and advances until it can close (assuming all child tasks have completed and obligations are resolved).
§Errors
Returns RegionCreateError if the parent is closed, missing, or at capacity.
Sourcepub async fn region_with_budget<P2, F, Fut, T, Caps>(
&self,
state: &mut RuntimeState,
_cx: &Cx<Caps>,
budget: Budget,
_policy: P2,
f: F,
) -> Result<Outcome<T, P2::Error>, RegionCreateError>
pub async fn region_with_budget<P2, F, Fut, T, Caps>( &self, state: &mut RuntimeState, _cx: &Cx<Caps>, budget: Budget, _policy: P2, f: F, ) -> Result<Outcome<T, P2::Error>, RegionCreateError>
Creates a child region with an explicit budget (met with the parent budget).
The effective budget is parent.meet(child) to ensure nested scopes can
never relax constraints.
Sourcepub async fn region_with_priority<P2, F, Fut, T, Caps>(
&self,
state: &mut RuntimeState,
_cx: &Cx<Caps>,
priority: RegionPriority,
_policy: P2,
f: F,
) -> Result<Outcome<T, P2::Error>, RegionCreateError>
pub async fn region_with_priority<P2, F, Fut, T, Caps>( &self, state: &mut RuntimeState, _cx: &Cx<Caps>, priority: RegionPriority, _policy: P2, f: F, ) -> Result<Outcome<T, P2::Error>, RegionCreateError>
Creates a child region with an explicit resource-pressure priority.
The child inherits this scope’s scheduler and capability budgets while classifying the admission request before pressure checks run.
Sourcepub async fn region_with_budget_and_priority<P2, F, Fut, T, Caps>(
&self,
state: &mut RuntimeState,
_cx: &Cx<Caps>,
budget: Budget,
priority: RegionPriority,
_policy: P2,
f: F,
) -> Result<Outcome<T, P2::Error>, RegionCreateError>
pub async fn region_with_budget_and_priority<P2, F, Fut, T, Caps>( &self, state: &mut RuntimeState, _cx: &Cx<Caps>, budget: Budget, priority: RegionPriority, _policy: P2, f: F, ) -> Result<Outcome<T, P2::Error>, RegionCreateError>
Creates a child region with explicit scheduler budget and pressure priority.
Sourcepub fn pipeline<I, E>(
&self,
cx: &Cx,
config: PipelineExecutionConfig,
inputs: I,
) -> PipelineExecution<'scope, I, I::Item, E, P>
pub fn pipeline<I, E>( &self, cx: &Cx, config: PipelineExecutionConfig, inputs: I, ) -> PipelineExecution<'scope, I, I::Item, E, P>
Build a streaming pipeline whose workers belong to this scope.
Every edge has an explicit capacity. The total in-flight limit holds
each input’s credit until the asynchronous sink acknowledges it,
including time spent queued, transforming, or awaiting consumption.
Append typed stages with then and start execution with run; building
the pipeline alone spawns no tasks. With no stages, inputs stream
directly to the sink.
Sourcepub async fn region_with_budget_and_capability_budget<P2, F, Fut, T, Caps>(
&self,
state: &mut RuntimeState,
_cx: &Cx<Caps>,
budget: Budget,
capability_budget: CapabilityBudget,
requirements: CapabilityBudgetRequirements,
_policy: P2,
f: F,
) -> Result<Outcome<T, P2::Error>, RegionCreateError>
pub async fn region_with_budget_and_capability_budget<P2, F, Fut, T, Caps>( &self, state: &mut RuntimeState, _cx: &Cx<Caps>, budget: Budget, capability_budget: CapabilityBudget, requirements: CapabilityBudgetRequirements, _policy: P2, f: F, ) -> Result<Outcome<T, P2::Error>, RegionCreateError>
Creates a child region with explicit scheduler and capability budgets.
Both budgets inherit from the parent scope and can only be tightened by the child-supplied envelopes. Required capability dimensions fail closed if neither the parent nor child supplies a non-exhausted envelope.
Sourcepub async fn join<T1, T2>(
&self,
cx: &Cx,
h1: TaskHandle<T1>,
h2: TaskHandle<T2>,
) -> (Result<T1, JoinError>, Result<T2, JoinError>)
pub async fn join<T1, T2>( &self, cx: &Cx, h1: TaskHandle<T1>, h2: TaskHandle<T2>, ) -> (Result<T1, JoinError>, Result<T2, JoinError>)
Sourcepub async fn race<T>(
&self,
cx: &Cx,
h1: TaskHandle<T>,
h2: TaskHandle<T>,
) -> Result<T, JoinError>
pub async fn race<T>( &self, cx: &Cx, h1: TaskHandle<T>, h2: TaskHandle<T>, ) -> Result<T, JoinError>
Races two task handles and returns the winner while draining the loser.
Sourcepub async fn hedge<F1, Fut1, F2, Fut2, T>(
&self,
state: &mut RuntimeState,
cx: &Cx,
delay: Duration,
primary: F1,
backup: F2,
) -> Result<T, JoinError>
pub async fn hedge<F1, Fut1, F2, Fut2, T>( &self, state: &mut RuntimeState, cx: &Cx, delay: Duration, primary: F1, backup: F2, ) -> Result<T, JoinError>
Hedges a primary operation with a backup operation.
- Spawns the primary task immediately.
- Waits for the delay.
- If primary finishes before delay: returns primary result.
- If delay fires: spawns backup task and races them.
The loser is cancelled and drained.
§Arguments
state- The runtime statecx- The capability contextdelay- The hedge delayprimary- The primary future factorybackup- The backup future factory
§Returns
Ok(T) if successful, Err(JoinError) if failed/cancelled.
Sourcepub async fn race_all<T>(
&self,
cx: &Cx,
handles: Vec<TaskHandle<T>>,
) -> Result<(T, usize), JoinError>
pub async fn race_all<T>( &self, cx: &Cx, handles: Vec<TaskHandle<T>>, ) -> Result<(T, usize), JoinError>
Races multiple tasks, waiting for the first to complete.
The winner’s result is returned. Losers are cancelled and drained.
§Arguments
cx- The capability contexthandles- Vector of task handles to race
§Returns
Ok((value, index)) if the winner succeeded.
Err(e) if the winner failed (error/cancel/panic).
Sourcepub async fn join_all<T>(
&self,
cx: &Cx,
handles: Vec<TaskHandle<T>>,
) -> Vec<Result<T, JoinError>>
pub async fn join_all<T>( &self, cx: &Cx, handles: Vec<TaskHandle<T>>, ) -> Vec<Result<T, JoinError>>
Joins multiple tasks, waiting for all to complete.
Returns a vector of results in the same order as the input handles.
Sourcepub fn defer_sync<F>(&self, state: &mut RuntimeState, f: F) -> bool
pub fn defer_sync<F>(&self, state: &mut RuntimeState, f: F) -> bool
Registers a synchronous finalizer to run when the region closes.
Finalizers are stored in LIFO order and executed during the Finalizing phase, after all children have completed. Use this for lightweight cleanup that doesn’t need to await.
§Arguments
state- The runtime statef- The synchronous cleanup function
§Returns
true if the finalizer was registered successfully.
§Example
scope.defer_sync(&mut state, || {
println!("Cleaning up!");
});Sourcepub fn defer_async<F>(&self, state: &mut RuntimeState, future: F) -> bool
pub fn defer_async<F>(&self, state: &mut RuntimeState, future: F) -> bool
Registers an asynchronous finalizer to run when the region closes.
Async finalizers run under a cancel mask to prevent interruption. They are driven to completion with a bounded budget. Use this for cleanup that needs to perform async operations (e.g., closing connections, flushing buffers).
§Arguments
state- The runtime statefuture- The async cleanup future
§Returns
true if the finalizer was registered successfully.
§Example
scope.defer_async(&mut state, async {
close_connection().await;
});Source§impl<P: Policy> Scope<'_, P>
impl<P: Policy> Scope<'_, P>
Sourcepub async fn quorum<T, E, I, F, Fut>(
&self,
cx: &Cx,
needed: usize,
branches: I,
) -> Result<Vec<T>, QuorumError<E>>
pub async fn quorum<T, E, I, F, Fut>( &self, cx: &Cx, needed: usize, branches: I, ) -> Result<Vec<T>, QuorumError<E>>
Runs branches concurrently in this scope’s region and resolves once
needed of them have returned Ok — M-of-N completion.
Every branch is spawned as a region task through the same
cancellation-dominant admission path JoinSet
uses, so a branch that returns a value after it was cancelled is
reported as Outcome::Cancelled, never as a late winner. Progress is
observed by polling every branch’s join handle each round, exactly like
Scope::race_all, and the wait ends at the first of:
neededbranches have returnedOk(quorum met);- so many branches have failed that
neededsuccesses can no longer happen (quorum impossible; seequorum_still_possible); - the caller’s
cxis cancelled.
In all three cases every still-running branch is then protocol-cancelled
(with CancelReason::quorum_met, or the caller’s own reason in case 3)
and joined to termination before this future returns — losers are
drained, never drop-abandoned, mirroring the race_all loser-drain
invariant. The drain is recorded in the loser-drain history when one is
wired (winner = the first successful branch in spawn order, or the
first branch when none succeeded).
The terminal outcomes are aggregated in spawn order through
quorum_outcomes and
quorum_to_result:
Ok(values): the successful values in spawn order. At leastneededvalues are returned; a branch that completed successfully in the same scheduling round as the deciding success (before cancellation could be requested) is included as well.QuorumError::InvalidQuorum:needed == 0orneeded > branches.len(). Rejected deterministically before any branch is spawned. The outcome folder keepsquorum(0, N)as its additive identity; this executable entry rejects it because “spawn N branches and immediately cancel them all” is never what a caller means.QuorumError::InsufficientSuccesses: quorum impossible; returned only after all branches terminated.QuorumError::Cancelled: the caller was cancelled (or a branch failed admission) before the quorum was met; every branch has been drained.QuorumError::Panicked: any branch — winner or drained loser — panicked. A panic outranks a met quorum, exactly as documented onquorum_to_result.
Branch factories must be Send + 'static (they run as spawned tasks);
heterogeneous branches can be boxed:
type Branch = Box<dyn FnOnce(Cx) -> Pin<Box<dyn Future<Output = Result<u32, String>> + Send>> + Send>;
let branches: Vec<Branch> = vec![
Box::new(|cx| Box::pin(replica_a(cx))),
Box::new(|cx| Box::pin(replica_b(cx))),
Box::new(|cx| Box::pin(replica_c(cx))),
];
let two_of_three = cx.scope().quorum(&cx, 2, branches).await?;§Errors
See the variant list above. Never panics on invalid needed.
Sourcepub async fn first_ok<T, E, I, F, Fut>(
&self,
cx: &Cx,
factories: I,
) -> Result<T, FirstOkError<E>>
pub async fn first_ok<T, E, I, F, Fut>( &self, cx: &Cx, factories: I, ) -> Result<T, FirstOkError<E>>
Tries factories sequentially — each one is invoked and its branch
run to a terminal outcome before the next is even constructed — and
resolves with the first Ok value.
Each attempt is spawned as a cancellation-dominant task in this scope’s
region (the same admission path JoinSet
uses) and joined to termination. Later factories are never invoked
once an attempt succeeds, so factories[i + 1] runs only if
factories[0..=i] all failed with Err. Before each attempt, and on
every poll while an attempt is in flight, the caller’s cx is
checkpointed: if the caller has been cancelled, the in-flight attempt is
protocol-cancelled with the caller’s reason and drained (joined to
termination) before the chain stops, and no further factory is invoked.
The terminal outcomes are aggregated through
FirstOkResult and
first_ok_to_result:
Ok(value): the first successful attempt’s value.FirstOkError::AllFailed: every attempt returnedErr; carries the errors in attempt order.FirstOkError::Cancelled: the caller was cancelled (or an attempt failed admission) before any attempt succeeded; carries the errors collected so far.FirstOkError::Panicked: an attempt panicked; the chain stops there.FirstOkError::Empty: no factories were supplied.
This is the runtime-backed counterpart of the inline first_ok!
macro: the macro awaits futures in place, this method runs each attempt
as a region task whose cancellation and drain are owned by the runtime.
Heterogeneous factories can be boxed exactly as for Scope::quorum.
§Errors
See the variant list above.
Sourcepub async fn timeout<T, E, F, Fut>(
&self,
cx: &Cx,
duration: Duration,
operation: F,
) -> Result<TimedResult<T, E>, SpawnError>
pub async fn timeout<T, E, F, Fut>( &self, cx: &Cx, duration: Duration, operation: F, ) -> Result<TimedResult<T, E>, SpawnError>
Runs operation as a task in this scope’s region with a deadline and
drains it if the deadline expires.
This is the drain-correct counterpart of
crate::time::timeout, which drops the inner future when the clock
wins. Here the operation is spawned as a region task; when
duration elapses first (or the caller is cancelled) the task is
protocol-cancelled with CancelReason::timeout and then joined
before this method returns, so the operation’s cleanup has run and the
region cannot observe an abandoned child.
The operation is spawned through the ordinary Cx::spawn_in path,
so the result classification follows the runtime’s acknowledged-value
rule: an operation that observes the cancellation (a failed
checkpoint) and still returns Ok/Err/panics has that terminal
outcome preserved as TimedResult::Completed (see
make_timed_result);
data produced after the deadline is surfaced rather than lost. Only an
operation that never acknowledged the cancellation (cancellation-blind,
or cancelled before its first poll) is reported as
TimedResult::TimedOut.
The deadline is measured on the scope’s clock (cx.now()), so lab
virtual time drives it deterministically.
§Errors
Returns the admission error if the operation cannot be spawned.
Source§impl<P: Policy> Scope<'_, P>
impl<P: Policy> Scope<'_, P>
Sourcepub fn spawn_gen_server<S: GenServer>(
&self,
state: &mut RuntimeState,
cx: &Cx,
server: S,
mailbox_capacity: usize,
) -> Result<(GenServerHandle<S>, StoredTask), SpawnError>
pub fn spawn_gen_server<S: GenServer>( &self, state: &mut RuntimeState, cx: &Cx, server: S, mailbox_capacity: usize, ) -> Result<(GenServerHandle<S>, StoredTask), SpawnError>
Spawns a new GenServer in this scope.
The server runs as a region-owned task. Calls and casts are delivered through a bounded MPSC channel with two-phase send semantics.
Sourcepub fn spawn_named_gen_server<S: GenServer>(
&self,
state: &mut RuntimeState,
cx: &Cx,
registry: &mut NameRegistry,
name: impl Into<String>,
server: S,
mailbox_capacity: usize,
now: Time,
) -> Result<(NamedGenServerHandle<S>, StoredTask), NamedSpawnError>
pub fn spawn_named_gen_server<S: GenServer>( &self, state: &mut RuntimeState, cx: &Cx, registry: &mut NameRegistry, name: impl Into<String>, server: S, mailbox_capacity: usize, now: Time, ) -> Result<(NamedGenServerHandle<S>, StoredTask), NamedSpawnError>
Spawns a named GenServer in this scope, registering it in the given
NameRegistry.
This combines spawn_gen_server with
NameRegistry::register into a
single atomic operation: the name is
acquired after the server task is created but before it starts
processing messages.
On success, the returned NamedGenServerHandle holds both the server
handle and the name lease. The lease is resolved when the handle is
released via NamedGenServerHandle::release_name or aborted via
NamedGenServerHandle::abort_lease.
§Errors
Returns NamedSpawnError::Spawn if the underlying task spawn fails,
or NamedSpawnError::NameTaken if the name is already registered.
In the name-taken case, the server task is not spawned (it is
abandoned before being stored).