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 automatic restart on failure.
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 during restart are buffered and processed
by the new instance.
§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<P: Policy> Scope<'_, P>
impl<P: Policy> Scope<'_, P>
Sourcepub fn spawn<F, Fut, Caps>(
&self,
state: &mut RuntimeState,
cx: &Cx<Caps>,
f: F,
) -> Result<(TaskHandle<Fut::Output>, StoredTask), SpawnError>
pub fn spawn<F, Fut, Caps>( &self, state: &mut RuntimeState, cx: &Cx<Caps>, f: F, ) -> Result<(TaskHandle<Fut::Output>, StoredTask), SpawnError>
Spawns a new task within this scope’s region.
This is the Task Tier spawn method for parallel execution. The task may migrate between worker threads, so all captured data must be thread-safe.
The task will be owned by the region and will be cancelled if the
region is cancelled. The returned TaskHandle can be used to await
the task’s result.
§Arguments
state- The runtime statecx- The capability context (used for tracing/authorization)f- A closure that produces the future, receiving the new task’sCx
§Returns
A TaskHandle<T> that can be used to await the task’s result.
§Soundness Rules (Type Bounds)
The following bounds encode the soundness rules for Send tasks:
F: FnOnce(Cx) -> Fut + Send + 'static- Factory called on any workerFut: Future + Send + 'static- Task may migrate between pollsFut::Output: Send + 'static- Result crosses thread boundary
These bounds ensure captured data can safely cross thread boundaries.
Use RRef<T> for region-heap-allocated data.
§Allowed Captures
| Type | Allowed | Reason |
|---|---|---|
String, Vec<T>, owned data | ✅ | Send + ’static by ownership |
Arc<T> where T: Send + Sync | ✅ | Thread-safe shared ownership |
RRef<T> | ✅ | Region-heap reference, Copy + Send |
Cx (cloned) | ✅ | Capability context is Send + Sync |
Rc<T>, RefCell<T> | ❌ | Not Send |
&T, &mut T | ❌ | Not ’static |
§Example
let handle = scope.spawn(&mut state, &cx, |cx| async move {
cx.trace("Child task running");
compute_value().await
});
let result = handle.join(&cx).await?;§Example with RRef
// Allocate expensive data in region heap
let index = region_record.heap_alloc(vec![1, 2, 3, 4, 5]);
let rref = RRef::<Vec<i32>>::new(region_id, index);
// RRef is Copy + Send, can be captured by multiple tasks
scope.spawn(&mut state, &cx, move |cx| async move {
// Would access via runtime state in real code
process_data(rref).await
});§Compile-Time Errors
Attempting to capture !Send types fails at compile time:
use std::rc::Rc;
fn require_send<T: Send>(_: &T) {}
fn test_rc_rejected<'r, P: asupersync::types::Policy>(
scope: &asupersync::cx::Scope<'r, P>,
state: &mut asupersync::runtime::RuntimeState,
cx: &asupersync::cx::Cx,
) {
let rc = Rc::new(42);
require_send(&rc);
let _ = scope.spawn(state, cx, move |_| async move {
let _ = rc; // Rc<i32> is not Send
});
}Attempting to capture non-'static references fails:
fn require_static<T: 'static>(_: T) {}
fn test_borrow_rejected<'r, P: asupersync::types::Policy>(
scope: &asupersync::cx::Scope<'r, P>,
state: &mut asupersync::runtime::RuntimeState,
cx: &asupersync::cx::Cx,
) {
let local = 42;
let borrow = &local;
require_static(borrow);
let _ = scope.spawn(state, cx, move |_| async move {
let _ = borrow; // &i32 is not 'static
});
}Sourcepub fn spawn_task<F, Fut, Caps>(
&self,
state: &mut RuntimeState,
cx: &Cx<Caps>,
f: F,
) -> Result<(TaskHandle<Fut::Output>, StoredTask), SpawnError>
pub fn spawn_task<F, Fut, Caps>( &self, state: &mut RuntimeState, cx: &Cx<Caps>, f: F, ) -> Result<(TaskHandle<Fut::Output>, StoredTask), SpawnError>
Spawns a Send task (explicit Task Tier API).
This is an explicit alias for spawn that makes the
execution tier clear in the API. Use this when you want to emphasize
that the task may migrate between workers.
§Type Bounds (Soundness Rules)
Same as spawn:
F: FnOnce(Cx) -> Fut + Send + 'staticFut: Future + Send + 'staticFut::Output: Send + 'static
§Example
// Explicit task tier spawn
let (handle, stored) = scope.spawn_task(&mut state, &cx, |cx| async move {
// This task may run on any worker
compute_parallel().await
})?;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 with the runtime state.
This is a convenience method that combines spawn() with
RuntimeState::store_spawned_task(). It’s the primary method
used by the spawn! macro.
§Arguments
state- The runtime state (for storing the task)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.
§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 fn spawn_local<F, Fut, Caps>(
&self,
state: &mut RuntimeState,
_cx: &Cx<Caps>,
f: F,
) -> Result<TaskHandle<Fut::Output>, SpawnError>
pub fn spawn_local<F, Fut, Caps>( &self, state: &mut RuntimeState, _cx: &Cx<Caps>, f: F, ) -> Result<TaskHandle<Fut::Output>, SpawnError>
Spawns a local (non-Send) task within this scope’s region (Fiber Tier).
This is the Fiber Tier spawn method. Local tasks are pinned to the
current worker thread and cannot be stolen by other workers. This enables
borrow-friendly execution with !Send types like Rc or RefCell.
§Execution Tier: Fiber
| Property | Value |
|---|---|
| Migration | Never (thread-pinned) |
| Send bound | Not required |
| Borrowing | Can capture &T (same-thread) |
| Use case | !Send types, borrowed data |
§Arguments
state- The runtime statecx- The capability contextf- A closure that produces the future, receiving the new task’sCx
§Panics
Panics if called from a blocking thread (spawn_blocking context).
§Example
use std::rc::Rc;
use std::cell::RefCell;
let counter = Rc::new(RefCell::new(0));
let counter_clone = counter.clone();
let handle = scope.spawn_local(&mut state, &cx, |cx| async move {
// Rc<RefCell<_>> is !Send but allowed in local tasks
*counter_clone.borrow_mut() += 1;
});Sourcepub fn spawn_blocking<F, R, Caps>(
&self,
state: &mut RuntimeState,
cx: &Cx<Caps>,
f: F,
) -> Result<(TaskHandle<R>, StoredTask), SpawnError>
pub fn spawn_blocking<F, R, Caps>( &self, state: &mut RuntimeState, cx: &Cx<Caps>, f: F, ) -> Result<(TaskHandle<R>, StoredTask), SpawnError>
Spawns a blocking operation on a dedicated thread pool.
This is used for CPU-bound or legacy synchronous operations that should not block async workers. The closure runs on a separate thread pool designed for blocking work.
§Arguments
state- The runtime statecx- The capability contextf- The blocking closure to run, receiving a context
§Type Bounds
F: FnOnce(Cx) -> R + Send + 'static- The closure must be SendR: Send + 'static- The result must be Send
§Example
let (handle, stored) = scope.spawn_blocking(&mut state, &cx, |cx| {
cx.trace("Starting blocking work");
// CPU-intensive work
expensive_computation()
});
let result = handle.join(&cx).await?;§Note
In Phase 0 (single-threaded), blocking operations run inline. A proper blocking pool is implemented in Phase 1+.
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 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 tasks, waiting for the first to complete.
The loser is cancelled and drained (awaited until it completes cancellation).
§Example
let (h1, _) = scope.spawn(...);
let (h2, _) = scope.spawn(...);
match scope.race(cx, h1, h2).await {
Ok(val) => println!("Winner result: {val}"),
Err(e) => println!("Race failed: {e}"),
}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 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.