Skip to main content

Cx

Struct Cx 

Source
pub struct Cx<Caps = CapSet<true, true, true, true, true>> { /* private fields */ }
Expand description

The capability context for a task.

Cx provides access to runtime capabilities within Asupersync. Runtime-managed effectful operations flow through Cx or narrower capability tokens, ensuring explicit capability security for the runtime boundary while keeping any host-boundary exceptions documented and scoped.

§Overview

A Cx instance is provided to each task by the runtime. It grants access to:

  • Identity: Query the current region and task IDs
  • Budget: Check remaining time/poll quotas
  • Cancellation: Observe and respond to cancellation requests
  • Tracing: Emit trace events for observability

§Cloning

Cx is cheaply clonable (it wraps an Arc). Clones share the same underlying state, so cancellation signals and budget updates are visible to all clones.

Implementations§

Source§

impl Cx

Source

pub fn current() -> Option<Cx>

Returns the current task context, if one is set.

This is set by the runtime while polling a task.

br-asupersync-5ckssb: walks the thread-local restriction stack to find the innermost installed context. If an outer scope pushed a restricted cx (via Cx::set_current_restricted or Cx::push_restriction), the returned cx carries the narrowed runtime mask, so cap-gated Option-returning methods (io, remote, timer_driver, fetch_cap) return None for any capability blocked by the restriction. This closes the ambient-authority leak that previously let untrusted call sites obtain a full-cap cx via the ambient lookup regardless of the type-level Caps parameter on the cx they were given as a function argument.

Returns None when no task context is installed and also during thread-local teardown, where the ambient context is no longer accessible.

Source

pub fn is_active() -> bool

Returns true iff a task context is installed on the current thread, without cloning any of the cx’s internal Arcs.

br-asupersync-xqt7dj: zero-Arc-clone existence check. Equivalent to Cx::current().is_some() but avoids the 3 atomic ops on the strong-count fields (inner, observability, handles) that Cx::current performs to materialize a returnable owned value. Use this for tight async polls that only need to detect whether they are running under a task context (e.g., diagnostic hooks).

Source

pub fn restriction_depth() -> usize

Returns the current thread-local Cx restriction-stack depth.

0 means no ambient context is installed. A plain runtime set_current contributes one unrestricted frame; nested Cx::set_current_restricted or push_restriction calls add frames that may narrow the ambient view observed by current.

Source

pub fn is_restricted() -> bool

Returns true when the ambient current Cx is currently mask-restricted.

This reports the thread-local ambient stack state, not the type-level Caps parameter of an arbitrary Cx value. To inspect a receiver’s effective type/runtime authority, use Cx::capabilities.

Source

pub fn with_current<F, R>(f: F) -> Option<R>
where F: FnOnce(&Cx) -> R,

Borrows the current task context for the duration of the closure.

br-asupersync-xqt7dj — zero-Arc-clone hot path for callers that only need to read from the active context (checkpoint(), trace(), now(), has_io(), etc.). The legacy Cx::current clones the three internal Arcs (3 atomic ops per call) so the returned cx can be retained across await points; for hot async loops that consult the ambient context many times per poll, that clone cost compounds.

with_current saves all 3 atomic ops in the common case (no active set_current_restricted / push_restriction narrowing — i.e. frame.mask == frame.cx.runtime_mask), borrowing the frame’s Cx directly and handing &Cx to the closure. When a restriction stack IS active and the frame’s mask differs from the cx’s runtime mask, we must apply the narrowed mask to a stack-local copy of the cx (1 cheap clone) so cap-gated Option-returning methods (io, remote, timer_driver, fetch_cap) observe the restriction; that case degrades to the same cost as legacy current(), never worse.

Lifetime semantics: the borrow on CURRENT_CX_STACK is held for the entire closure body, so the closure cannot install a new ambient cx via set_current* (the inner mutable borrow would panic). Use Cx::current (which clones) when the ambient cx must outlive a single read or be moved into an async block.

Restriction-mask correctness: the borrowed/cloned cx observes the active frame’s mask, so callers running under a set_current_restricted scope see the narrowed cap view via with_current exactly as they would via current().clone().

Returns None when no ambient context is installed (or during thread-local teardown); the closure is then NOT invoked.

Source

pub fn set_current(cx: Option<Cx>) -> CurrentCxGuard

Installs cx as the current ambient task context for the duration of the returned guard.

Pushes a new frame onto the thread-local stack with the FULL capability mask. For installations that should narrow the ambient view (e.g. when handing control to untrusted code that should not see full caps), use Cx::set_current_restricted instead.

This is the public ambient-install primitive: the scheduler calls it once per poll to mirror a task’s owned Cx into the thread-local, and first-party bridges such as asupersync-tokio-compat call it from outside this crate to make a held Cx visible to tokio-shaped code that reaches for Cx::current. It is capability-safe — the caller can only install a Cx it already holds, so no authority is minted out of band (unlike the test-internals-gated standalone constructors); narrow the ambient view with Cx::set_current_restricted. Because the compat bridge ships as a separate crate, this must be pub at the crate boundary rather than pub(crate) (asupersync#49).

Source§

impl<Caps> Cx<Caps>
where Caps: CapSetRuntimeMask,

Source

pub fn set_current_restricted(self) -> CurrentCxGuard

Push this cx onto the thread-local restriction stack with its OWN runtime mask (computed from the type-level Caps parameter). While the returned guard is alive, any ambient Cx::current() lookup observes the narrowed mask — even if the underlying FullCx it wraps has every capability bit set internally.

br-asupersync-5ckssb: this is the tooling that gives the Cx::current() ambient defense its teeth. A function that is about to delegate to less-trusted code can do

let _guard = restricted_cx.set_current_restricted();
untrusted::do_work(); // ambient Cx::current() observes the
                      // restricted cap mask

and be confident that the callee cannot escape its capability budget via a thread-local lookup.

Source

pub fn capabilities(&self) -> CapabilitySnapshot

Returns the capabilities visible through this context.

The top-level booleans in the returned CapabilitySnapshot are the effective authority of this receiver: the type-level capability row intersected with the runtime mask carried by this Cx. When a context is obtained via Cx::current under set_current_restricted or Cx::push_restriction, the runtime mask already includes that ambient restriction.

Source§

impl Cx

Source

pub fn push_restriction(mask: CapMask) -> CurrentCxGuard

Push an explicit cap::CapMask restriction onto the thread-local stack without changing the underlying cx.

The mask is intersected with the currently-active mask (whatever Cx::current() would otherwise return). While the guard is alive, ambient lookups observe the narrowed view. Useful for short scoped restrictions (“disable IO across this callback”) without requiring a separate restricted-cap cx instance.

(br-asupersync-5ckssb)

Source§

impl<Caps> Cx<Caps>

Source

pub fn new(region: RegionId, task: TaskId, budget: Budget) -> Cx<Caps>

Creates a new capability context (internal use).

Source

pub fn new_with_observability( region: RegionId, task: TaskId, budget: Budget, observability: Option<ObservabilityState>, io_driver: Option<IoDriverHandle>, entropy: Option<Arc<dyn EntropySource>>, ) -> Cx<Caps>

Creates a new capability context with optional observability state (internal use).

Source

pub fn new_with_io( region: RegionId, task: TaskId, budget: Budget, observability: Option<ObservabilityState>, io_driver: Option<IoDriverHandle>, io_cap: Option<Arc<dyn IoCap>>, entropy: Option<Arc<dyn EntropySource>>, ) -> Cx<Caps>

Creates a new capability context with optional I/O capability (internal use).

Source

pub fn new_with_drivers( region: RegionId, task: TaskId, budget: Budget, observability: Option<ObservabilityState>, io_driver: Option<IoDriverHandle>, io_cap: Option<Arc<dyn IoCap>>, timer_driver: Option<TimerDriverHandle>, entropy: Option<Arc<dyn EntropySource>>, ) -> Cx<Caps>

Creates a new capability context with optional I/O and timer drivers (internal use).

Source

pub fn io_driver_handle(&self) -> Option<IoDriverHandle>

Returns a cloned handle to the I/O driver, if present.

Source

pub fn restrict<NewCaps>(&self) -> Cx<NewCaps>
where NewCaps: SubsetOf<Caps>,

Re-type this context to a narrower capability set.

This is a zero-cost type-level restriction. It does not change runtime behavior, but removes access to gated APIs at compile time.

Source

pub fn with_remote_cap(self, cap: RemoteCap) -> Cx<Caps>

Attaches a remote capability to this context.

This allows the context to perform remote operations like spawn_remote.

Source

pub fn with_pressure(self, pressure: Arc<SystemPressure>) -> Cx<Caps>

Attach a system pressure handle for compute budget propagation.

The handle is shared via Arc so all clones observe the same pressure state. A monitor thread can call SystemPressure::set_headroom to update the value, and any code with &Cx can read it lock-free.

Source

pub fn pressure(&self) -> Option<&SystemPressure>

Read the current system pressure, if attached.

Returns None if no pressure handle was attached to this context.

Source

pub fn registry_handle(&self) -> Option<RegistryHandle>

Returns the registry capability handle, if attached.

Source

pub fn has_registry(&self) -> bool

Returns true if a registry handle is attached.

Source

pub fn with_evidence_sink(self, sink: Option<Arc<dyn EvidenceSink>>) -> Cx<Caps>

Attaches an evidence sink for runtime decision tracing.

Source

pub fn emit_evidence(&self, entry: &EvidenceLedger)

Emit an evidence entry to the attached sink, if any.

This is a no-op if no evidence sink is configured. Errors during emission are handled internally by the sink (logged and dropped).

Source

pub fn with_macaroon(self, token: MacaroonToken) -> Cx<Caps>

Attaches a Macaroon capability token to this context.

The token is stored in an Arc for cheap cloning. Child contexts created via restrict or retype inherit the macaroon.

Source

pub fn macaroon(&self) -> Option<&MacaroonToken>

Returns a reference to the attached Macaroon token, if any.

Source

pub fn attenuate(&self, predicate: CaveatPredicate) -> Option<Cx<Caps>>

Attenuate the capability token by adding a caveat.

Returns a new Cx with an attenuated macaroon. The original context is unchanged. This does not require the root key — any holder can add caveats (but nobody can remove them).

Returns None if no macaroon is attached or the caveat cannot be encoded in the macaroon wire format.

Source

pub fn attenuate_time_limit(&self, deadline_ms: u64) -> Option<Cx<Caps>>

Attenuate with a time limit: the token expires at deadline_ms.

Convenience wrapper around attenuate with [CaveatPredicate::TimeBefore].

Returns None if no macaroon is attached.

Source

pub fn attenuate_scope(&self, pattern: impl Into<String>) -> Option<Cx<Caps>>

Attenuate with a resource scope restriction.

The pattern uses simple glob syntax: * matches any single segment, ** matches any number of segments.

Returns None if no macaroon is attached or pattern exceeds the macaroon wire-format length cap.

Source

pub fn attenuate_rate_limit( &self, max_count: u32, window_secs: u32, ) -> Option<Cx<Caps>>

Attenuate with a windowed rate limit.

Restricts the token to at most max_count uses per window_secs. The caller is responsible for tracking the sliding window and providing both the observed window duration and use count in VerificationContext.

Returns None if no macaroon is attached.

Source

pub fn attenuate_from_budget(&self) -> Option<Cx<Caps>>

Attenuate with the Cx’s current budget deadline.

If the Cx has a finite deadline, adds a TimeBefore caveat using it. If no deadline is set, the macaroon is returned unchanged.

Returns None if no macaroon is attached.

Source

pub fn verify_capability( &self, root_key: &AuthKey, expected_identifier: &str, context: &VerificationContext, ) -> Result<(), VerificationError>

Verify the attached capability token against a root key, expected capability identifier, and runtime context.

Checks the HMAC chain integrity and evaluates all caveat predicates. Emits evidence to the attached sink on both success and failure.

Returns Ok(()) if the token is valid and all caveats pass.

§Errors

Returns VerificationError if verification fails (bad signature or failed caveat). Returns Err(VerificationError::InvalidSignature) if no macaroon is attached.

Source

pub fn logical_now(&self) -> LogicalTime

Returns the current logical time without ticking.

Source

pub fn logical_tick(&self) -> LogicalTime

Records a local logical event and returns the updated time.

Source

pub fn logical_receive(&self, sender_time: &LogicalTime) -> LogicalTime

Merges a received logical time and returns the updated time.

Source

pub fn timer_driver(&self) -> Option<TimerDriverHandle>
where Caps: HasTime,

Returns a cloned handle to the timer driver, if present.

The timer driver provides access to timer registration for async time operations like sleep, timeout, and interval. When present, these operations use the runtime’s timer wheel instead of spawning threads.

§Example
if let Some(timer) = Cx::current().and_then(|cx| cx.timer_driver()) {
    let deadline = timer.now() + Duration::from_secs(1);
    let handle = timer.register(deadline, waker);
}
Source

pub fn has_timer(&self) -> bool
where Caps: HasTime,

Returns true if a timer driver is available.

When true, time operations can use the runtime’s timer wheel. When false, time operations fall back to OS-level timing.

Source

pub fn io(&self) -> Option<&dyn IoCap>
where Caps: HasIo,

Returns the I/O capability, if one is configured.

The I/O capability provides access to async I/O operations. If no capability is configured, this returns None and I/O operations are not available.

§Capability Model

Asupersync uses explicit capability-based I/O:

  • Production runtime configures real I/O capability (via reactor)
  • Lab runtime can configure virtual I/O for deterministic testing
  • Code that needs I/O must explicitly check for and use this capability
§Example
async fn read_data(cx: &Cx) -> io::Result<Vec<u8>> {
    let io = cx.io().ok_or_else(|| {
        io::Error::new(io::ErrorKind::Unsupported, "I/O not available")
    })?;

    // Use io capability...
    Ok(vec![])
}
Source

pub fn has_io(&self) -> bool
where Caps: HasIo,

Returns true if I/O capability is available.

Convenience method to check if I/O operations can be performed.

Source

pub fn fetch_cap(&self) -> Option<&dyn FetchIoCap>
where Caps: HasIo,

Returns the fetch adapter capability, if one is configured.

This is the browser-facing network authority surface. When present, requests must pass explicit origin/method/credential policy checks before any host fetch operation is attempted.

Source

pub fn has_fetch_cap(&self) -> bool
where Caps: HasIo,

Returns true if a fetch adapter capability is available.

Source

pub fn remote(&self) -> Option<&RemoteCap>
where Caps: HasRemote,

Returns the remote capability, if one is configured.

The remote capability authorizes spawning tasks on remote nodes. Without this capability, spawn_remote returns RemoteError::NoCapability.

§Capability Model

Remote execution is an explicit capability:

  • Production runtime configures remote capability with transport config
  • Lab runtime can configure it for deterministic distributed testing
  • Code that needs remote spawning must check for this capability
Source

pub fn has_remote(&self) -> bool
where Caps: HasRemote,

Returns true if the remote capability is available.

Convenience method to check if remote task operations can be performed.

Source

pub fn register_io<S>( &self, source: &S, interest: Interest, ) -> Result<IoRegistration, Error>
where S: Source, Caps: HasIo,

Registers an I/O source with the reactor for the given interest.

This method registers a source (such as a socket or file descriptor) with the reactor so that the task can be woken when I/O operations are ready.

§Arguments
  • source - The I/O source to register (must implement Source)
  • interest - The I/O operations to monitor for (read, write, or both)
§Returns

Returns a IoRegistration handle that represents the active registration. When dropped, the registration is automatically deregistered from the reactor.

§Errors

Returns an error if:

  • No reactor is available (reactor not initialized or not present)
  • The reactor fails to register the source
Source

pub fn region_id(&self) -> RegionId

Returns the current region ID.

The region ID identifies the structured concurrency scope that owns this task. Useful for debugging and for associating task-specific data with region boundaries.

§Example
fn log_context(cx: &Cx) {
    println!("Running in region: {:?}", cx.region_id());
}
Source

pub fn task_id(&self) -> TaskId

Returns the current task ID.

The task ID uniquely identifies this task within the runtime. Useful for debugging, tracing, and correlating log entries.

§Example
fn log_task(cx: &Cx) {
    println!("Task {:?} starting work", cx.task_id());
}
Source

pub fn task_type(&self) -> Option<String>

Returns the task type label, if one has been set.

Task types are optional metadata used by adaptive deadline monitoring and metrics to group similar work.

Source

pub fn set_task_type(&self, task_type: impl Into<String>)

Sets a task type label for adaptive monitoring and metrics.

This is intended to be called early in task execution to associate a stable label with the task’s behavior profile.

§Policy (br-asupersync-9vpwpc)

task_type is exported VERBATIM as an OpenTelemetry label by the observability layer (see src/observability/otel.rs). To prevent cardinality explosion against the metrics backend AND PII leakage into telemetry pipelines, the value MUST be a fixed, low-cardinality identifier:

  • Length ≤ 64 bytes
  • Charset: ASCII alphanumeric, _, ., -, : only (no whitespace, no high-entropy characters typical of PII like email addresses, UUIDs, or user IDs).
  • First character: ASCII letter (matches OpenTelemetry naming conventions and rejects formats like "-leading-dash").

Values that violate the policy are SILENTLY DROPPED with a tracing::warn! log instead of being stored — set_task_type’s public signature returns () so we cannot surface the rejection as Err. The warn includes a length-truncated preview so the developer can find their offending call site without the full PII content showing up in production logs again.

Source

pub fn budget(&self) -> Budget

Returns the current budget.

The budget defines resource limits for this task:

  • deadline: Absolute time limit
  • poll_quota: Maximum number of polls
  • cost_quota: Abstract cost units
  • priority: Scheduling priority

Frameworks can use the budget to implement request timeouts:

§Example
async fn check_timeout(cx: &Cx) -> Result<(), TimeoutError> {
    let budget = cx.budget();
    if budget.is_expired() {
        return Err(TimeoutError::DeadlineExceeded);
    }
    Ok(())
}
Source

pub fn capability_budget(&self) -> CapabilityBudget

Returns the explicit capability/resource budget carried by this context.

Source

pub fn budget_for_timeout(&self, timeout: Duration) -> Budget
where Caps: HasTime,

Returns the ambient budget tightened by a relative timeout.

now comes from this context’s clock (the timer driver in production, virtual time in the lab), so the result is correct and deterministic under both. Composition uses Budget::tightened_by_timeout: the resulting deadline is min(ambient_deadline, now + timeout) — a per-call timeout can never loosen an outer deadline. All other budget dimensions are inherited unchanged.

This is the canonical way to derive per-operation (request, statement, RPC) budgets for deadline propagation.

§Example
async fn query(cx: &Cx, sql: &str) -> Result<Rows, DbError> {
    // 30s statement timeout, but never beyond the request deadline.
    let budget = cx.budget_for_timeout(Duration::from_secs(30));
    run_with_budget(cx, budget, sql).await
}
Source

pub fn remaining_budget(&self) -> RemainingBudget

Returns a point-in-time snapshot of this context’s remaining budget.

Thin wrapper over Budget::remaining using this context’s clock via now_for_observability, so no HasTime capability is required — this is a diagnostic read. The live budget’s quotas drain as the task runs; the snapshot is valid only at the moment of the call.

Canonical use is adaptive control flow: “do I have time and polls left for another retry?”

Source

pub fn budget_stats(&self) -> BudgetStats

Returns point-in-time budget accounting for this context.

The snapshot is consistent at call time only. Quotas and deadlines may be consumed immediately after this method returns. Before copying the budget fields, this drains pending fast-path checkpoint counters into the authoritative checkpoint state so diagnostic budget reads do not leave checkpoint accounting lagging behind fast-path progress.

Source

pub fn plan_child_capability_budget( &self, child: CapabilityBudget, requirements: CapabilityBudgetRequirements, ) -> Result<CapabilityBudget, CapabilityBudgetRefusal>

Computes the effective child capability budget without mutating this context.

Required dimensions fail closed if no parent or child budget supplies them, or if the effective envelope is already exhausted.

Source

pub fn apply_child_capability_budget( &self, child: CapabilityBudget, requirements: CapabilityBudgetRequirements, ) -> Result<CapabilityBudget, CapabilityBudgetRefusal>

Applies a child capability budget to this context after fail-closed validation.

This mutates the shared CxInner, so all clones observe the same effective envelope. Use Self::plan_child_capability_budget when a caller only needs an admission decision.

Source

pub fn is_cancel_requested(&self) -> bool

Returns true if cancellation has been requested.

This is a non-blocking check that queries whether a cancellation signal has been sent to this task. Unlike checkpoint(), this method does not return an error - it just reports the current state.

Frameworks should check this periodically during long-running operations to enable graceful shutdown.

§Example
async fn process_items(cx: &Cx, items: Vec<Item>) -> Result<(), Error> {
    for item in items {
        // Check for cancellation between items
        if cx.is_cancel_requested() {
            return Err(Error::Cancelled);
        }
        process(item).await?;
    }
    Ok(())
}
Source

pub fn checkpoint(&self) -> Result<(), Error>

Checks for cancellation and returns an error if cancelled.

This is a checkpoint where cancellation can be observed. It combines checking the cancellation flag with returning an error, making it convenient for use with the ? operator.

In addition to cancellation checking, this method records progress by updating the checkpoint state. This is useful for:

  • Detecting stuck/stalled tasks via checkpoint_state()
  • Work-stealing scheduler decisions
  • Observability and debugging

If the context is currently masked (via masked()), this method returns Ok(()) even when cancellation is pending, deferring the cancellation until the mask is released.

§Errors

Returns an Err with kind ErrorKind::Cancelled if cancellation is pending and the context is not masked.

§Example
async fn do_work(cx: &Cx) -> Result<(), Error> {
    // Use checkpoint with ? for concise cancellation handling
    cx.checkpoint()?;

    expensive_operation().await?;

    cx.checkpoint()?;

    another_operation().await?;

    Ok(())
}

Implements rule.cancel.checkpoint_masked (#10): if cancel_requested and mask_depth == 0, acknowledge cancellation. If mask_depth > 0, cancel remains deferred until mask is unwound.

Source

pub fn checkpoint_with(&self, msg: impl Into<String>) -> Result<(), Error>

Checks for cancellation with a progress message.

This is like checkpoint() but also records a human-readable message describing the current progress. The message is stored in the checkpoint state and can be retrieved via checkpoint_state().

§Errors

Returns an Err with kind ErrorKind::Cancelled if cancellation is pending and the context is not masked.

§Example
async fn process_batch(cx: &Cx, items: &[Item]) -> Result<(), Error> {
    for (i, item) in items.iter().enumerate() {
        cx.checkpoint_with(format!("Processing item {}/{}", i + 1, items.len()))?;
        process(item).await?;
    }
    Ok(())
}
Source

pub fn checkpoint_state(&self) -> CheckpointState

Returns a snapshot of the current checkpoint state.

The checkpoint state tracks progress reporting checkpoints:

  • last_checkpoint: The runtime time when the last checkpoint was recorded
  • last_message: The message from the last checkpoint_with() call
  • checkpoint_count: Total number of checkpoints

This is useful for monitoring task progress and detecting stalled tasks.

§Example
fn check_task_health(cx: &Cx) -> bool {
    let state = cx.checkpoint_state();
    state.last_checkpoint.is_some()
}
Source

pub fn checkpoint_history(&self) -> Vec<(Time, String)>

Returns the oldest-to-newest history of message checkpoints.

Messageless checkpoint calls update checkpoint count and time only; they do not allocate or append history entries.

§Example
cx.checkpoint_with("connected")?;
cx.checkpoint_with("querying")?;
let trail = cx.checkpoint_history();
assert_eq!(trail.last().map(|(_, msg)| msg.as_str()), Some("querying"));
Source

pub fn now(&self) -> Time
where Caps: HasTime,

Returns the current physical time according to the configured timer driver, or the wall clock if no timer driver is available.

Source

pub fn now_for_observability(&self) -> Time

Returns the current time from the configured timer driver, falling back to wall-clock when no driver is installed.

Unlike [now], this method does not require the HasTime capability. It is intended for observability/diagnostic code that wants replayable timestamps in lab mode without threading a HasTime-capable Cx through. Production behavior is identical to now.

Source

pub fn masked<F, R>(&self, f: F) -> R
where F: FnOnce() -> R,

Executes a closure with cancellation masked.

While masked, checkpoint() will return Ok(()) even if cancellation has been requested. This is used for critical sections that must not be interrupted, such as:

  • Completing a two-phase commit
  • Flushing buffered data
  • Releasing resources in a specific order

Masking can be nested - each call to masked() increments a depth counter, and cancellation is only observable when depth returns to 0.

§Example
async fn commit_transaction(cx: &Cx, tx: Transaction) -> Result<(), Error> {
    // Critical section: must complete even if cancelled
    cx.masked(|| {
        tx.prepare()?;
        tx.commit()?;  // Cannot be interrupted here
        Ok(())
    })
}
§Note

Use masking sparingly. Long-masked sections defeat the purpose of responsive cancellation. Prefer short critical sections followed by a checkpoint.

Invariant inv.cancel.mask_monotone (#12): mask_depth is monotonically non-increasing during cancel processing. The increment here occurs before cancel acknowledgement; MaskGuard::drop decrements via saturating_sub(1). Invariant inv.cancel.mask_bounded (#11): mask_depth <= MAX_MASK_DEPTH.

Source

pub fn trace(&self, message: &str)

Traces an event for observability.

Trace events are associated with the current task and region, enabling structured observability. In the lab runtime, traces are captured deterministically for replay and debugging.

§Example
async fn process_request(cx: &Cx, request: &Request) -> Response {
    cx.trace("Request received");

    let result = handle(request).await;

    cx.trace("Request processed");

    result
}
§Note

When a trace buffer is attached to this Cx, this writes a structured user trace event into that buffer and also emits to the log collector. Without a trace buffer, it still records the log entry.

Source

pub fn trace_with_fields(&self, message: &str, fields: &[(&str, &str)])

Logs a trace-level message with structured key-value fields.

Each field is attached to the resulting LogEntry, making it queryable in the log collector.

§Example
cx.trace_with_fields("request handled", &[
    ("method", "GET"),
    ("path", "/api/users"),
    ("status", "200"),
]);
Source

pub fn enter_span(&self, name: &str) -> SpanGuard<Caps>

Enters a named span, returning a guard that ends the span on drop.

The span forks the current DiagnosticContext, assigning a new SpanId with the previous span as parent. When the guard is dropped the original context is restored.

§Example
{
    let _guard = cx.enter_span("parse_request");
    // ... work inside the span ...
} // span ends here
Source

pub fn set_request_id(&self, id: impl Into<String>)

Sets a request correlation ID on the diagnostic context.

The ID propagates to all log entries and child spans created from this context, enabling end-to-end request tracing.

Source

pub fn request_id(&self) -> Option<String>

Returns the current request correlation ID, if set.

Source

pub fn log(&self, entry: LogEntry)

Logs a structured entry to the attached collector, if present.

Source

pub fn diagnostic_context(&self) -> DiagnosticContext

Returns a snapshot of the current diagnostic context.

Source

pub fn set_diagnostic_context(&self, ctx: DiagnosticContext)

Replaces the current diagnostic context.

Source

pub fn set_log_collector(&self, collector: LogCollector)

Attaches a log collector to this context.

Source

pub fn log_collector(&self) -> Option<LogCollector>

Returns the current log collector, if attached.

Source

pub fn set_trace_buffer(&self, trace: TraceBufferHandle)

Attaches a trace buffer to this context.

Source

pub fn trace_buffer(&self) -> Option<TraceBufferHandle>

Returns the current trace buffer handle, if attached.

Source

pub fn entropy(&self) -> &(dyn EntropySource + 'static)
where Caps: HasRandom,

Returns the entropy source for this context.

Source

pub fn random_u64(&self) -> u64
where Caps: HasRandom,

Generates a random u64 using the context entropy source.

Source

pub fn random_bytes(&self, dest: &mut [u8])
where Caps: HasRandom,

Fills a buffer with random bytes using the context entropy source.

Source

pub fn random_usize(&self, bound: usize) -> usize
where Caps: HasRandom,

Generates a random usize in [0, bound) with rejection sampling.

Source

pub fn random_bool(&self) -> bool
where Caps: HasRandom,

Generates a random boolean.

Source

pub fn random_f64(&self) -> f64
where Caps: HasRandom,

Generates a random f64 in [0, 1).

Source

pub fn shuffle<T>(&self, slice: &mut [T])
where Caps: HasRandom,

Shuffles a slice in place using Fisher-Yates.

Source

pub fn set_cancel_requested(&self, value: bool)

Sets the cancellation flag for testing purposes.

This method allows tests to simulate cancellation signals. It sets the cancel_requested flag, which will cause subsequent checkpoint() calls to return an error (unless masked).

§Example
use asupersync::Cx;

let cx = Cx::for_testing();
assert!(cx.checkpoint().is_ok());

cx.set_cancel_requested(true);
assert!(cx.checkpoint().is_err());
§Note

This API is intended for testing only. In production, cancellation signals are propagated by the runtime through the task tree.

Source

pub fn cancel_with(&self, kind: CancelKind, message: Option<&'static str>)

Cancels this context with a detailed reason.

This is the preferred method for initiating cancellation, as it provides complete attribution information. The reason includes:

  • The kind of cancellation (e.g., User, Timeout, Deadline)
  • An optional message explaining the cancellation
  • Origin region and task information (automatically set)
§Arguments
  • kind - The type of cancellation being initiated
  • message - An optional human-readable message explaining why
§Example
use asupersync::{Cx, types::CancelKind};

let cx = Cx::for_testing();
cx.cancel_with(CancelKind::User, Some("User pressed Ctrl+C"));
assert!(cx.is_cancel_requested());

if let Some(reason) = cx.cancel_reason() {
    assert_eq!(reason.kind, CancelKind::User);
}
§Note

This method only sets the local cancellation flag. In a real runtime, cancellation propagates through the region tree via cancel_request().

Source

pub fn cancel_fast(&self, kind: CancelKind)

Cancels without building a full attribution chain (performance-critical path).

Use this when attribution isn’t needed and minimizing allocations is important. The cancellation reason will have minimal attribution (kind + region only).

§Performance

This method avoids:

  • Message string allocation
  • Cause chain allocation
  • Timestamp lookup

Use cancel_with when you need full attribution for debugging.

§Example
use asupersync::{Cx, types::CancelKind};

let cx = Cx::for_testing();

// Fast cancellation - no allocation
cx.cancel_fast(CancelKind::RaceLost);
assert!(cx.is_cancel_requested());
Source

pub fn cancel_reason(&self) -> Option<CancelReason>

Gets the cancellation reason if this context is cancelled.

Returns None if the context is not cancelled, or Some(reason) if cancellation has been requested. The returned reason includes full attribution (kind, origin region, origin task, timestamp, cause chain).

§Example
use asupersync::{Cx, types::CancelKind};

let cx = Cx::for_testing();
assert!(cx.cancel_reason().is_none());

cx.cancel_with(CancelKind::Timeout, Some("request timeout"));
if let Some(reason) = cx.cancel_reason() {
    assert_eq!(reason.kind, CancelKind::Timeout);
    println!("Cancelled: {:?}", reason.kind);
}
Source

pub fn cancel_chain(&self) -> impl Iterator<Item = CancelReason>

Iterates through the full cancellation cause chain.

The first element is the immediate reason, followed by parent causes in order (immediate -> root). This is useful for understanding the full propagation path of a cancellation.

Returns an empty iterator if the context is not cancelled.

§Example
use asupersync::{Cx, types::{CancelKind, CancelReason}};

let cx = Cx::for_testing();

// Create a chained reason: ParentCancelled -> Deadline
let root_cause = CancelReason::deadline();
let chained = CancelReason::parent_cancelled().with_cause(root_cause);

// Set it via internal method for testing
cx.set_cancel_reason(chained);

let chain: Vec<_> = cx.cancel_chain().collect();
assert_eq!(chain.len(), 2);
assert_eq!(chain[0].kind, CancelKind::ParentCancelled);
assert_eq!(chain[1].kind, CancelKind::Deadline);
Source

pub fn root_cancel_cause(&self) -> Option<CancelReason>

Gets the root cause of cancellation.

This is the original trigger that initiated the cancellation, regardless of how many parent regions the cancellation propagated through. For example, if a grandchild task was cancelled due to a parent timeout, root_cancel_cause() returns the original Timeout reason, not the intermediate ParentCancelled reasons.

Returns None if the context is not cancelled.

§Example
use asupersync::{Cx, types::{CancelKind, CancelReason}};

let cx = Cx::for_testing();

// Simulate a deep cancellation chain
let deadline = CancelReason::deadline();
let parent1 = CancelReason::parent_cancelled().with_cause(deadline);
let parent2 = CancelReason::parent_cancelled().with_cause(parent1);

cx.set_cancel_reason(parent2);

// Root cause is the original Deadline, not ParentCancelled
if let Some(root) = cx.root_cancel_cause() {
    assert_eq!(root.kind, CancelKind::Deadline);
}
Source

pub fn cancelled_by(&self, kind: CancelKind) -> bool

Checks if cancellation was due to a specific kind.

This checks the immediate reason only, not the cause chain. For example, if a task was cancelled with ParentCancelled due to an upstream timeout, cancelled_by(CancelKind::ParentCancelled) returns true but cancelled_by(CancelKind::Timeout) returns false.

Use any_cause_is() to check the full cause chain.

§Example
use asupersync::{Cx, types::CancelKind};

let cx = Cx::for_testing();
cx.cancel_with(CancelKind::User, Some("manual cancel"));

assert!(cx.cancelled_by(CancelKind::User));
assert!(!cx.cancelled_by(CancelKind::Timeout));
Source

pub fn any_cause_is(&self, kind: CancelKind) -> bool

Checks if any cause in the chain is a specific kind.

This searches the entire cause chain, from the immediate reason to the root cause. This is useful for checking if a specific condition (like a timeout) anywhere in the hierarchy caused this cancellation.

§Example
use asupersync::{Cx, types::{CancelKind, CancelReason}};

let cx = Cx::for_testing();

// Grandchild cancelled due to parent timeout
let timeout = CancelReason::timeout();
let parent_cancelled = CancelReason::parent_cancelled().with_cause(timeout);

cx.set_cancel_reason(parent_cancelled);

// Immediate reason is ParentCancelled, but timeout is in the chain
assert!(cx.cancelled_by(CancelKind::ParentCancelled));
assert!(!cx.cancelled_by(CancelKind::Timeout));  // immediate only
assert!(cx.any_cause_is(CancelKind::Timeout));   // searches chain
assert!(cx.any_cause_is(CancelKind::ParentCancelled));  // also in chain
Source

pub fn set_cancel_reason(&self, reason: CancelReason)

Sets the cancellation reason (for testing purposes).

This method allows tests to set a specific cancellation reason, including complex cause chains. It sets both the cancel_requested flag and the cancel_reason.

§Example
use asupersync::{Cx, types::{CancelKind, CancelReason}};

let cx = Cx::for_testing();

// Create a chained reason for testing
let root = CancelReason::deadline();
let chained = CancelReason::parent_cancelled().with_cause(root);

cx.set_cancel_reason(chained);

assert!(cx.is_cancel_requested());
assert_eq!(cx.cancel_reason().unwrap().kind, CancelKind::ParentCancelled);
Source

pub async fn race<T>( &self, futures: Vec<Pin<Box<dyn Future<Output = T> + Send>>>, ) -> Result<T, JoinError>

Races multiple futures, waiting for the first to complete.

This method is used by the race! macro. It runs the provided futures concurrently (inline, not spawned) and returns the result of the first one to complete. Losers are dropped (cancelled).

§Cancellation vs Draining

This method drops the losing futures, which cancels them. However, unlike Scope::race, it does not await the losers to ensure they have fully cleaned up (“drained”).

If you are racing TaskHandles and require the “Losers are drained” invariant (parent waits for losers to terminate), use Scope::race or Scope::race_all instead.

Source

pub async fn race_named<T>( &self, futures: Vec<(&'static str, Pin<Box<dyn Future<Output = T> + Send>>)>, ) -> Result<T, JoinError>

Races multiple named futures.

Similar to race, but accepts names for tracing purposes.

§Cancellation vs Draining

This method drops the losing futures, which cancels them. However, unlike Scope::race, it does not await the losers to ensure they have fully cleaned up (“drained”).

Source

pub async fn race_timeout<T>( &self, duration: Duration, futures: Vec<Pin<Box<dyn Future<Output = T> + Send>>>, ) -> Result<T, JoinError>
where Caps: HasTime,

Races multiple futures with a timeout.

If the timeout expires before any future completes, returns a cancellation error.

§Cancellation vs Draining

This method drops the losing futures (or all futures on timeout), which cancels them. However, it does not await the losers to ensure they have fully cleaned up (“drained”).

Source

pub async fn race_timeout_named<T>( &self, duration: Duration, futures: Vec<(&'static str, Pin<Box<dyn Future<Output = T> + Send>>)>, ) -> Result<T, JoinError>
where Caps: HasTime,

Races multiple named futures with a timeout.

§Cancellation vs Draining

This method drops the losing futures (or all futures on timeout), which cancels them. However, it does not await the losers to ensure they have fully cleaned up (“drained”).

Source

pub fn scope(&self) -> Scope<'static>

Creates a Scope bound to this context’s region.

The returned Scope can be used to spawn tasks, create child regions, and register finalizers. All spawned tasks will be owned by this context’s region.

§Example
// Using the scope! macro (recommended):
scope!(cx, {
    let handle = cx.spawn_in(&scope, |cx| async { 42 });
    handle.await
});

// Manual scope creation:
let scope = cx.scope();
// Use scope for spawning...
§Note

In Phase 0, this creates a scope bound to the current region. In later phases, the scope! macro will create child regions with proper quiescence guarantees.

Source

pub fn scope_with_budget(&self, budget: Budget) -> Scope<'static>

Creates a Scope bound to this context’s region with a custom budget.

This is used by the scope! macro when a budget is specified:

scope!(cx, budget: Budget::with_deadline_at_secs(5), {
    // body
})
Source

pub fn scope_with_budget_and_capability_budget( &self, budget: Budget, capability_budget: CapabilityBudget, requirements: CapabilityBudgetRequirements, ) -> Result<Scope<'static>, CapabilityBudgetRefusal>

Creates a Scope with explicit scheduler and capability budgets.

The scheduler budget is clamped with Budget::meet semantics by Self::scope_with_budget. The capability budget is planned against the context’s current capability envelope and fails closed when a required dimension is absent or exhausted.

Source§

impl Cx

Source

pub async fn race_drained<T>( &self, futures: Vec<Pin<Box<dyn Future<Output = T> + Send>>>, ) -> Result<T, JoinError>
where T: Send + 'static,

Races multiple inline futures with loser-drain semantics — the drain-correct engine behind the race! macro.

Unlike Cx::race, which merely drops (cancels) the losing futures, this entry point spawns every branch as a task in this context’s region and resolves them through Scope::race_all. The first branch to complete wins; every loser is then protocol-cancelled and drained — awaited to termination — before this future returns. This is the project’s “losers are drained” invariant headline: resources held by a losing branch (obligations, finalizers, file handles) are resolved, not abandoned.

Because branches run as spawned tasks, each must be Send + 'static and the output T must be Send + 'static, and this context must be runtime-wired (carry a spawn gateway). A branch that fails admission fails the race closed with JoinError::Cancelled; already-spawned siblings are cancelled as the race future unwinds.

On an empty branch list this is pending until the context is cancelled, mirroring Cx::race.

Source

pub async fn race_drained_named<T>( &self, futures: Vec<(&'static str, Pin<Box<dyn Future<Output = T> + Send>>)>, ) -> Result<T, JoinError>
where T: Send + 'static,

Races multiple named inline futures with loser-drain semantics.

Names are accepted for source-level symmetry with Cx::race_named; the drain machinery itself is name-agnostic. See Cx::race_drained for the full guarantee.

Source

pub async fn race_drained_timeout<T>( &self, duration: Duration, futures: Vec<Pin<Box<dyn Future<Output = T> + Send>>>, ) -> Result<T, JoinError>
where T: Send + 'static,

Races multiple inline futures with loser-drain semantics and a timeout.

If duration elapses before any branch completes, the whole race future is abandoned: every branch is cancelled by drop. The loser-drain guarantee applies to the ordinary win path; the timeout path mirrors Cx::race_timeout (cancel-on-drop, no post-timeout drain).

Source

pub async fn race_drained_timeout_named<T>( &self, duration: Duration, futures: Vec<(&'static str, Pin<Box<dyn Future<Output = T> + Send>>)>, ) -> Result<T, JoinError>
where T: Send + 'static,

Races multiple named inline futures with loser-drain semantics and a timeout. See Cx::race_drained_timeout.

Source§

impl Cx<CapSet<false, false, false, false, false>>

Source

pub fn detached_cancel_context() -> Cx<CapSet<false, false, false, false, false>>

Creates a detached context that carries cancellation and budget state but no runtime effect capabilities.

This is for adapters and CLI diagnostics that need to exercise cancellation-aware primitives outside a running task. It deliberately returns Cx<cap::None> and installs an empty runtime capability mask, so it cannot provide spawn, timer, random, I/O, or remote authority. The synthetic IDs are non-root so an accidental immediate-completion path cannot create a root-scoped obligation.

Source§

impl<Caps> Cx<Caps>
where Caps: HasSpawn + Send + Sync + 'static,

Source

pub fn spawn<F, Fut>( &self, f: F, ) -> Result<TaskHandle<<Fut as Future>::Output>, SpawnError>
where F: FnOnce(Cx<Caps>) -> Fut + Send + 'static, Fut: Future + Send + 'static, <Fut as Future>::Output: Send + 'static,

Spawns a task into this Cx’s own region without touching the RuntimeState lock (br-asupersync-hwjqyo / A2.2).

The factory receives its own child Cx — built by mailbox admission with the canonical task identity, then overlaid with this parent’s inherited capabilities (observability fork, entropy fork, io-cap/registry/remote/blocking/evidence/macaroon/pressure handles, capability budget, runtime mask) — the same inheritance set as Scope::build_child_task_cx.

§Targeting: Cx::spawn vs Cx::spawn_in vs state-threaded boot

Cx::spawn is ambient-within-my-region: the child joins the calling task’s region and is drained/cancelled with it. To target a specific scope’s region through the same lock-free path, use Cx::spawn_in. When you need an explicit structural boundary (a child region that closes to quiescence before you proceed), create the region via a Scope. If you are unsure which you want, you want the Scope.

§Errors

Returns [SpawnError::RuntimeUnavailable] when this Cx carries no spawn gateway or region counter (e.g. built by a harness without runtime wiring). Admission-time denials (region closing, quota) resolve through the returned handle as JoinError::Cancelled. Never panics.

Source

pub fn spawn_in<F, Fut, P>( &self, scope: &Scope<'_, P>, f: F, ) -> Result<TaskHandle<<Fut as Future>::Output>, SpawnError>
where P: Policy, F: FnOnce(Cx<Caps>) -> Fut + Send + 'static, Fut: Future + Send + 'static, <Fut as Future>::Output: Send + 'static,

Spawns a task into scope’s region without touching the RuntimeState lock (br-asupersync-hwjqyo / A2.2).

This is the scope-targeting sibling of Cx::spawn: the child task is owned by scope’s region (joining its drain/cancel lifecycle and its close-to-quiescence accounting) and runs under scope’s budget (budget_source = "scope", matching the state-threaded boot path), while capability inheritance and the factory-receives-its-own-Cx discipline come from this context exactly as in Cx::spawn.

§Targeting: Cx::spawn vs Cx::spawn_in vs state-threaded boot

Use Cx::spawn to stay in your own region, Cx::spawn_in to target a scope’s region through the same lock-free mailbox path. Use Scope::spawn_registered(&mut RuntimeState, ..) only for synchronous boot paths that still need inline task-record inspection.

§Example
// Inside a running task: `cx` is this task's context and `scope`
// was produced by runtime-wired scope creation (e.g. `cx.scope()`
// or a child-region scope), so it carries its region's counter.
let handle = cx.spawn_in(&scope, |child| async move {
    child.checkpoint()?;
    Outcome::ok(42)
})?;
let joined = handle.join(&cx).await;
§Errors

Returns [SpawnError::RuntimeUnavailable] when this Cx carries no spawn gateway, or when scope carries no pending-spawn counter for its region (e.g. a scope built without runtime wiring whose region differs from this context’s). Admission-time denials (region closing, quota) resolve through the returned handle as JoinError::Cancelled. Never panics.

Source

pub fn spawn_registered_in<F, Fut, P>( &self, scope: &Scope<'_, P>, f: F, ) -> Result<TaskHandle<<Fut as Future>::Output>, SpawnError>
where P: Policy, F: FnOnce(Cx<Caps>) -> Fut + Send + 'static, Fut: Future + Send + 'static, <Fut as Future>::Output: Send + 'static,

Spawns a task into scope’s region and registers it through gateway admission.

This is the v2, Cx-side replacement for call sites that used Scope::spawn_registered(&mut RuntimeState, ..) only to avoid hand-calling RuntimeState::store_spawned_task. The returned handle is pending until mailbox admission runs: before admission, TaskHandle::task_id reports a provisional mailbox id; after admission, it reports the canonical runtime task id. Registration/storage happens in the admission path, not synchronously at the call site.

Use this when a runtime-wired Cx is available and the caller does not need synchronous supervisor-boot failure observation. Synchronous boot paths that must observe child start failure inline still need the state-threaded path until that protocol is redesigned.

§Errors

Returns [SpawnError::RuntimeUnavailable] when this Cx carries no spawn gateway, or when scope carries no pending-spawn counter for its region (see Cx::spawn_in). Admission-time denials resolve through the returned handle as JoinError::Cancelled.

Source

pub fn spawn_blocking<F, R>(&self, f: F) -> Result<TaskHandle<R>, SpawnError>
where F: FnOnce(Cx<Caps>) -> R + Send + 'static, R: Send + 'static,

Spawns a blocking closure into this Cx’s own region without touching the RuntimeState lock (br-asupersync-wwyi9k / A2.2b).

Admission runs through the same producer-side gateway as Cx::spawn, so the blocking work is owned by a real region task (region close waits for it; pending-spawn accounting applies) and the closure receives its own child Cx carrying this parent’s inherited capabilities and budget. Execution dispatches to the runtime blocking pool when this context carries a pool handle; without one (e.g. under the lab runtime) the closure runs inline inside the admitted task — the same deterministic fallback as the free spawn_blocking and the removed legacy Scope::spawn_blocking.

§Cancel safety

Cancelling the owning region cancels the wrapper task; a closure already running on the pool is not preempted (its result is discarded), matching the documented spawn_blocking semantics. A panic inside the closure resolves the handle as JoinError::Panicked and the task outcome as Panicked.

§Errors

Returns [SpawnError::RuntimeUnavailable] when this Cx carries no spawn gateway or region counter. Admission-time denials (region closing, quota) resolve through the returned handle as JoinError::Cancelled. Never panics.

Source

pub fn spawn_blocking_in<F, R, P>( &self, scope: &Scope<'_, P>, f: F, ) -> Result<TaskHandle<R>, SpawnError>
where P: Policy, F: FnOnce(Cx<Caps>) -> R + Send + 'static, R: Send + 'static,

Spawns a blocking closure into scope’s region without touching the RuntimeState lock (br-asupersync-wwyi9k / A2.2b).

The scope-targeting sibling of Cx::spawn_blocking: region ownership, budget, and pending-spawn accounting come from scope exactly as in Cx::spawn_in; pool dispatch and the deterministic inline fallback match Cx::spawn_blocking.

§Errors

Returns [SpawnError::RuntimeUnavailable] when this Cx carries no spawn gateway, or when scope carries no pending-spawn counter for its region (see Cx::spawn_in). Admission-time denials resolve through the returned handle as JoinError::Cancelled. Never panics.

Source

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

Spawns a !Send task into this Cx’s own region, pinned to the current worker thread (br-asupersync-i9y5wb / A2.2a).

The v2 sibling of the removed legacy Scope::spawn_local: no &mut RuntimeState at the call site. The factory is parked on the calling worker’s thread-local spawn lane and admitted by that same worker at its next dispatch point — the factory and its future never cross threads. The admitted task is pinned to this worker, stored thread-locally, and scheduled on the non-stealable local queue, so steal paths reject migration structurally.

§Targeting

Like Cx::spawn, the child joins this context’s region. Use Cx::spawn_local_in to target a specific scope’s region.

§Errors

Returns [SpawnError::LocalSchedulerUnavailable] when the calling thread is not a runtime worker (local spawns require an owner worker; this includes the lab runtime and blocking-pool threads — unlike the legacy path this never panics). Returns [SpawnError::RuntimeUnavailable] when this Cx carries no spawn gateway or region counter. Admission-time denials (region closing, quota) resolve through the returned handle as JoinError::Cancelled.

Source

pub fn spawn_local_in<F, Fut, P>( &self, scope: &Scope<'_, P>, f: F, ) -> Result<TaskHandle<<Fut as Future>::Output>, SpawnError>
where P: Policy, F: FnOnce(Cx<Caps>) -> Fut + 'static, Fut: Future + 'static, <Fut as Future>::Output: Send + 'static,

Spawns a !Send task into scope’s region, pinned to the current worker thread (br-asupersync-i9y5wb / A2.2a).

The scope-targeting sibling of Cx::spawn_local: region ownership, budget, and pending-spawn accounting come from scope exactly as in Cx::spawn_in.

§Errors

As Cx::spawn_local, plus [SpawnError::RuntimeUnavailable] when scope carries no pending-spawn counter for its region (see Cx::spawn_in).

Source§

impl Cx

Source

pub fn for_testing() -> Cx

Creates a capability context for testing purposes.

This constructor creates a Cx with default IDs and an infinite budget, suitable for unit and integration tests. The resulting context is fully functional but not connected to a real runtime. The synthetic region is non-root so tests can create cancel-safe obligations without tripping the root-region leak guard.

§Example
use asupersync::Cx;

let cx = Cx::for_testing();
assert!(!cx.is_cancel_requested());
assert!(cx.checkpoint().is_ok());
§Note

This API is intended for testing only. Production code should receive Cx instances from the runtime, not construct them directly.

§Visibility (br-asupersync-2x6hbi)

Gated behind #[cfg(any(test, feature = "test-internals"))] so that production consumers of the asupersync crate cannot construct a Cx<cap::All> out of band, bypassing runtime cap-mask enforcement. Tests still see the constructor through cfg(test), and explicit dev-time consumers can opt in with --features test-internals.

Source

pub fn for_testing_with_budget(budget: Budget) -> Cx

Creates a test-only capability context with a specified budget.

Similar to Self::for_testing() but allows specifying a custom budget for testing timeout behavior.

§Example
use asupersync::{Cx, Budget, Time};

// Create a context with a 30-second deadline
let cx = Cx::for_testing_with_budget(
    Budget::new().with_deadline(Time::from_secs(30))
);
§Note

This API is intended for testing only. Production code should receive Cx instances from the runtime, not construct them directly. Gated behind cfg(any(test, feature = "test-internals")) (br-asupersync-2x6hbi).

Source

pub fn for_testing_with_io() -> Cx

Creates a test-only capability context with lab I/O capability.

This constructor creates a Cx with a LabIoCap for testing I/O code paths without performing real I/O.

§Example
use asupersync::Cx;

let cx = Cx::for_testing_with_io();
assert!(cx.has_io());
assert!(!cx.io().unwrap().is_real_io());
§Note

This API is intended for testing only. Gated behind cfg(any(test, feature = "test-internals")) (br-asupersync-2x6hbi).

Source

pub fn for_request_with_budget(budget: Budget) -> Cx

Creates a request-scoped capability context with a specified budget.

br-asupersync-ovztin: this constructor is now gated behind cfg(any(test, feature = "test-internals")). The pre-fix shape was fully pub and produced a Cx with CapMask::all() and freshly-minted ephemeral region/task IDs — i.e. a fully ambient capability source available to any caller in any crate. The doc comment claimed the resulting Cx “still carries the runtime cap-mask, so it cannot escalate beyond what the request handler was granted at the boundary”; that claim was false because Cx::new -> Cx::new_with_drivers constructed the runtime_mask as CapMask::all() without looking at any parent Cx.

Concrete escape paths the previous shape allowed:

  • External-crate capability injection. Any crate linking asupersync could call Cx::for_request_with_budget(Budget:: INFINITE) from a Drop impl, panic handler, or sync helper and get full Time / IO / blocking-pool / entropy / remote_cap access.
  • Sandbox escape from restricted Cx. A handler holding a mask-narrowed Cx could call this to mint a fresh all-capabilities Cx and bypass the restriction entirely.
  • **Compounds with br-asupersync-3lk5n2 (now closed): the ephemeral task is also not in state.tasks, so oracles / deadline monitor / futurelock detector all silently miss the request.

Production callers that need a request-scoped Cx must go through crate::runtime::Runtime::request_cx_with_budget, which inherits the runtime’s drivers and cap-mask via build_request_cx_from_inner and is therefore non-escalating.

Default builds exclude test-internals, so production consumers lose access to this constructor entirely unless they opt in explicitly. The only ambient-free way to mint a Cx in production is through the runtime boundary, which is capability-controlled.

Source

pub fn for_request() -> Cx

Creates a request-scoped capability context with an infinite budget.

br-asupersync-ovztin: see Self::for_request_with_budget for the cfg-gating rationale; this is the infinite-budget convenience wrapper and is gated identically.

Source

pub fn for_testing_with_remote(cap: RemoteCap) -> Cx

Creates a test-only capability context with a remote capability.

This constructor creates a Cx with a RemoteCap for testing remote task spawning without a real network transport.

§Note

This API is intended for testing only. Gated behind cfg(any(test, feature = "test-internals")) (br-asupersync-2x6hbi).

Source§

impl<Caps> Cx<Caps>
where Caps: Send + Sync + 'static,

Source

pub fn scoped_cpu<'env, F, R>( &'env self, worker_cap: usize, f: F, ) -> Result<R, ScopedCpuError>
where F: for<'scope> FnOnce(&ScopedCpu<'scope, 'env, Caps>) -> R,

Run a BLOCKING, lending fork-join region: f receives a ScopedCpu whose ScopedCpu::spawn accepts non-'static (borrowing) child closures; scoped_cpu returns only after every child joined. See the module docs for the soundness and scope-tree arguments and the blocking discipline.

worker_cap is the structured budget on child threads for this region (spawn calls beyond it are refused, never queued).

§Errors

Trait Implementations§

Source§

impl<Caps> Clone for Cx<Caps>

Source§

fn clone(&self) -> Cx<Caps>

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<Caps> Debug for Cx<Caps>
where Caps: Debug,

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<Caps = CapSet<true, true, true, true, true>> !RefUnwindSafe for Cx<Caps>

§

impl<Caps = CapSet<true, true, true, true, true>> !UnwindSafe for Cx<Caps>

§

impl<Caps> Freeze for Cx<Caps>

§

impl<Caps> Send for Cx<Caps>

§

impl<Caps> Sync for Cx<Caps>

§

impl<Caps> Unpin for Cx<Caps>

§

impl<Caps> UnsafeUnpin for Cx<Caps>

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> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. 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> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
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<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

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