Skip to main content

Scope

Struct Scope 

Source
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>

Source

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 creation
  • cx - Capability context
  • actor - The actor instance
  • mailbox_capacity - Bounded mailbox size
§Returns

A tuple of (ActorHandle, StoredTask). The StoredTask must be registered with the runtime via state.store_spawned_task().

Source

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>
where A: Actor, F: FnMut() -> A + Send + 'static,

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 creation
  • cx - Capability context
  • factory - 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>

Source

pub fn new(region: RegionId, budget: Budget) -> Self

Creates a new scope (internal use).

Source

pub fn region_id(&self) -> RegionId

Returns the region ID for this scope.

Source

pub fn budget(&self) -> Budget

Returns the budget for this scope.

Source

pub fn spawn<F, Fut, Caps>( &self, state: &mut RuntimeState, cx: &Cx<Caps>, f: F, ) -> Result<(TaskHandle<Fut::Output>, StoredTask), SpawnError>
where Caps: HasSpawn + Send + Sync + 'static, F: FnOnce(Cx<Caps>) -> Fut + Send + 'static, Fut: Future + Send + 'static, Fut::Output: Send + 'static,

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 state
  • cx - The capability context (used for tracing/authorization)
  • f - A closure that produces the future, receiving the new task’s Cx
§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 worker
  • Fut: Future + Send + 'static - Task may migrate between polls
  • Fut::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
TypeAllowedReason
String, Vec<T>, owned dataSend + ’static by ownership
Arc<T> where T: Send + SyncThread-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 TNot ’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
    });
}
Source

pub fn spawn_task<F, Fut, Caps>( &self, state: &mut RuntimeState, cx: &Cx<Caps>, f: F, ) -> Result<(TaskHandle<Fut::Output>, StoredTask), SpawnError>
where Caps: HasSpawn + Send + Sync + 'static, F: FnOnce(Cx<Caps>) -> Fut + Send + 'static, Fut: Future + Send + 'static, Fut::Output: Send + 'static,

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 + 'static
  • Fut: Future + Send + 'static
  • Fut::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
})?;
Source

pub fn spawn_registered<F, Fut, Caps>( &self, state: &mut RuntimeState, cx: &Cx<Caps>, f: F, ) -> Result<TaskHandle<Fut::Output>, SpawnError>
where Caps: HasSpawn + Send + Sync + 'static, F: FnOnce(Cx<Caps>) -> Fut + Send + 'static, Fut: Future + Send + 'static, Fut::Output: Send + 'static,

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’s Cx
§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?;
Source

pub fn spawn_local<F, Fut, Caps>( &self, state: &mut RuntimeState, _cx: &Cx<Caps>, f: F, ) -> Result<TaskHandle<Fut::Output>, SpawnError>
where Caps: HasSpawn + Send + Sync + 'static, F: FnOnce(Cx<Caps>) -> Fut + 'static, Fut: Future + 'static, Fut::Output: Send + 'static,

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
PropertyValue
MigrationNever (thread-pinned)
Send boundNot required
BorrowingCan capture &T (same-thread)
Use case!Send types, borrowed data
§Arguments
  • state - The runtime state
  • cx - The capability context
  • f - A closure that produces the future, receiving the new task’s Cx
§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;
});
Source

pub fn spawn_blocking<F, R, Caps>( &self, state: &mut RuntimeState, cx: &Cx<Caps>, f: F, ) -> Result<(TaskHandle<R>, StoredTask), SpawnError>
where Caps: HasSpawn + Send + Sync + 'static, F: FnOnce(Cx<Caps>) -> R + Send + 'static, R: Send + 'static,

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 state
  • cx - The capability context
  • f - The blocking closure to run, receiving a context
§Type Bounds
  • F: FnOnce(Cx) -> R + Send + 'static - The closure must be Send
  • R: 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+.

Source

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>
where P2: Policy, F: FnOnce(Scope<'_, P2>, &mut RuntimeState) -> Fut, Fut: Future<Output = Outcome<T, P2::Error>>,

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.

Source

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>
where P2: Policy, F: FnOnce(Scope<'_, P2>, &mut RuntimeState) -> Fut, Fut: Future<Output = Outcome<T, P2::Error>>,

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.

Source

pub async fn join<T1, T2>( &self, cx: &Cx, h1: TaskHandle<T1>, h2: TaskHandle<T2>, ) -> (Result<T1, JoinError>, Result<T2, JoinError>)

Joins two tasks, waiting for both to complete.

This method waits for both tasks to complete, regardless of their outcome. It returns a tuple of results.

§Example
let (h1, _) = scope.spawn(...);
let (h2, _) = scope.spawn(...);
let (r1, r2) = scope.join(cx, h1, h2).await;
Source

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}"),
}
Source

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 context
  • handles - Vector of task handles to race
§Returns

Ok((value, index)) if the winner succeeded. Err(e) if the winner failed (error/cancel/panic).

Source

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.

Source

pub fn defer_sync<F>(&self, state: &mut RuntimeState, f: F) -> bool
where F: FnOnce() + Send + 'static,

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 state
  • f - The synchronous cleanup function
§Returns

true if the finalizer was registered successfully.

§Example
scope.defer_sync(&mut state, || {
    println!("Cleaning up!");
});
Source

pub fn defer_async<F>(&self, state: &mut RuntimeState, future: F) -> bool
where F: Future<Output = ()> + Send + 'static,

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 state
  • future - 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>

Source

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.

Trait Implementations§

Source§

impl<P: Policy> Debug for Scope<'_, P>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<'r, P> Freeze for Scope<'r, P>

§

impl<'r, P> RefUnwindSafe for Scope<'r, P>
where P: RefUnwindSafe,

§

impl<'r, P> Send for Scope<'r, P>

§

impl<'r, P> Sync for Scope<'r, P>

§

impl<'r, P> Unpin for Scope<'r, P>

§

impl<'r, P> UnwindSafe for Scope<'r, P>
where P: RefUnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, _span: NoopSpan) -> Self

Instruments this future with a span (no-op when disabled).
Source§

fn in_current_span(self) -> Self

Instruments this future with the current span (no-op when disabled).
Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more