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
impl Cx
Sourcepub fn current() -> Option<Cx>
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.
Sourcepub fn is_active() -> bool
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).
Sourcepub fn restriction_depth() -> usize
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.
Sourcepub fn is_restricted() -> bool
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.
Sourcepub fn with_current<F, R>(f: F) -> Option<R>
pub fn with_current<F, R>(f: F) -> Option<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.
Sourcepub fn set_current(cx: Option<Cx>) -> CurrentCxGuard
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,
impl<Caps> Cx<Caps>where
Caps: CapSetRuntimeMask,
Sourcepub fn set_current_restricted(self) -> CurrentCxGuard
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 maskand be confident that the callee cannot escape its capability budget via a thread-local lookup.
Sourcepub fn capabilities(&self) -> CapabilitySnapshot
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
impl Cx
Sourcepub fn push_restriction(mask: CapMask) -> CurrentCxGuard
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>
impl<Caps> Cx<Caps>
Sourcepub fn new(region: RegionId, task: TaskId, budget: Budget) -> Cx<Caps>
pub fn new(region: RegionId, task: TaskId, budget: Budget) -> Cx<Caps>
Creates a new capability context (internal use).
Sourcepub fn new_with_observability(
region: RegionId,
task: TaskId,
budget: Budget,
observability: Option<ObservabilityState>,
io_driver: Option<IoDriverHandle>,
entropy: Option<Arc<dyn EntropySource>>,
) -> Cx<Caps>
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).
Sourcepub 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>
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).
Sourcepub 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>
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).
Sourcepub fn io_driver_handle(&self) -> Option<IoDriverHandle>
pub fn io_driver_handle(&self) -> Option<IoDriverHandle>
Returns a cloned handle to the I/O driver, if present.
Sourcepub fn restrict<NewCaps>(&self) -> Cx<NewCaps>where
NewCaps: SubsetOf<Caps>,
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.
Sourcepub fn with_remote_cap(self, cap: RemoteCap) -> Cx<Caps>
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.
Sourcepub fn with_pressure(self, pressure: Arc<SystemPressure>) -> Cx<Caps>
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.
Sourcepub fn pressure(&self) -> Option<&SystemPressure>
pub fn pressure(&self) -> Option<&SystemPressure>
Read the current system pressure, if attached.
Returns None if no pressure handle was attached to this context.
Sourcepub fn registry_handle(&self) -> Option<RegistryHandle>
pub fn registry_handle(&self) -> Option<RegistryHandle>
Returns the registry capability handle, if attached.
Sourcepub fn has_registry(&self) -> bool
pub fn has_registry(&self) -> bool
Returns true if a registry handle is attached.
Sourcepub fn with_evidence_sink(self, sink: Option<Arc<dyn EvidenceSink>>) -> Cx<Caps>
pub fn with_evidence_sink(self, sink: Option<Arc<dyn EvidenceSink>>) -> Cx<Caps>
Attaches an evidence sink for runtime decision tracing.
Sourcepub fn emit_evidence(&self, entry: &EvidenceLedger)
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).
Sourcepub fn with_macaroon(self, token: MacaroonToken) -> Cx<Caps>
pub fn with_macaroon(self, token: MacaroonToken) -> Cx<Caps>
Sourcepub fn macaroon(&self) -> Option<&MacaroonToken>
pub fn macaroon(&self) -> Option<&MacaroonToken>
Returns a reference to the attached Macaroon token, if any.
Sourcepub fn attenuate(&self, predicate: CaveatPredicate) -> Option<Cx<Caps>>
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.
Sourcepub fn attenuate_time_limit(&self, deadline_ms: u64) -> Option<Cx<Caps>>
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.
Sourcepub fn attenuate_scope(&self, pattern: impl Into<String>) -> Option<Cx<Caps>>
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.
Sourcepub fn attenuate_rate_limit(
&self,
max_count: u32,
window_secs: u32,
) -> Option<Cx<Caps>>
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.
Sourcepub fn attenuate_from_budget(&self) -> Option<Cx<Caps>>
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.
Sourcepub fn verify_capability(
&self,
root_key: &AuthKey,
expected_identifier: &str,
context: &VerificationContext,
) -> Result<(), VerificationError>
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.
Sourcepub fn logical_now(&self) -> LogicalTime
pub fn logical_now(&self) -> LogicalTime
Returns the current logical time without ticking.
Sourcepub fn logical_tick(&self) -> LogicalTime
pub fn logical_tick(&self) -> LogicalTime
Records a local logical event and returns the updated time.
Sourcepub fn logical_receive(&self, sender_time: &LogicalTime) -> LogicalTime
pub fn logical_receive(&self, sender_time: &LogicalTime) -> LogicalTime
Merges a received logical time and returns the updated time.
Sourcepub fn timer_driver(&self) -> Option<TimerDriverHandle>where
Caps: HasTime,
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);
}Sourcepub fn has_timer(&self) -> boolwhere
Caps: HasTime,
pub fn has_timer(&self) -> boolwhere
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.
Sourcepub fn io(&self) -> Option<&dyn IoCap>where
Caps: HasIo,
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![])
}Sourcepub fn has_io(&self) -> boolwhere
Caps: HasIo,
pub fn has_io(&self) -> boolwhere
Caps: HasIo,
Returns true if I/O capability is available.
Convenience method to check if I/O operations can be performed.
Sourcepub fn fetch_cap(&self) -> Option<&dyn FetchIoCap>where
Caps: HasIo,
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.
Sourcepub fn has_fetch_cap(&self) -> boolwhere
Caps: HasIo,
pub fn has_fetch_cap(&self) -> boolwhere
Caps: HasIo,
Returns true if a fetch adapter capability is available.
Sourcepub fn remote(&self) -> Option<&RemoteCap>where
Caps: HasRemote,
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
Sourcepub fn has_remote(&self) -> boolwhere
Caps: HasRemote,
pub fn has_remote(&self) -> boolwhere
Caps: HasRemote,
Returns true if the remote capability is available.
Convenience method to check if remote task operations can be performed.
Sourcepub fn register_io<S>(
&self,
source: &S,
interest: Interest,
) -> Result<IoRegistration, Error>
pub fn register_io<S>( &self, source: &S, interest: Interest, ) -> Result<IoRegistration, Error>
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 implementSource)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
Sourcepub fn task_type(&self) -> Option<String>
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.
Sourcepub fn set_task_type(&self, task_type: impl Into<String>)
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.
Sourcepub fn budget(&self) -> Budget
pub fn budget(&self) -> Budget
Returns the current budget.
The budget defines resource limits for this task:
deadline: Absolute time limitpoll_quota: Maximum number of pollscost_quota: Abstract cost unitspriority: 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(())
}Sourcepub fn capability_budget(&self) -> CapabilityBudget
pub fn capability_budget(&self) -> CapabilityBudget
Returns the explicit capability/resource budget carried by this context.
Sourcepub fn budget_for_timeout(&self, timeout: Duration) -> Budgetwhere
Caps: HasTime,
pub fn budget_for_timeout(&self, timeout: Duration) -> Budgetwhere
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
}Sourcepub fn remaining_budget(&self) -> RemainingBudget
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?”
Sourcepub fn budget_stats(&self) -> BudgetStats
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.
Sourcepub fn plan_child_capability_budget(
&self,
child: CapabilityBudget,
requirements: CapabilityBudgetRequirements,
) -> Result<CapabilityBudget, CapabilityBudgetRefusal>
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.
Sourcepub fn apply_child_capability_budget(
&self,
child: CapabilityBudget,
requirements: CapabilityBudgetRequirements,
) -> Result<CapabilityBudget, CapabilityBudgetRefusal>
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.
Sourcepub fn is_cancel_requested(&self) -> bool
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(())
}Sourcepub fn checkpoint(&self) -> Result<(), Error>
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.
Sourcepub fn checkpoint_with(&self, msg: impl Into<String>) -> Result<(), Error>
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(())
}Sourcepub fn checkpoint_state(&self) -> CheckpointState
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 recordedlast_message: The message from the lastcheckpoint_with()callcheckpoint_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()
}Sourcepub fn checkpoint_history(&self) -> Vec<(Time, String)>
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"));Sourcepub fn now(&self) -> Timewhere
Caps: HasTime,
pub fn now(&self) -> Timewhere
Caps: HasTime,
Returns the current physical time according to the configured timer driver, or the wall clock if no timer driver is available.
Sourcepub fn now_for_observability(&self) -> Time
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.
Sourcepub fn masked<F, R>(&self, f: F) -> Rwhere
F: FnOnce() -> R,
pub fn masked<F, R>(&self, f: F) -> Rwhere
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.
Sourcepub fn trace(&self, message: &str)
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.
Sourcepub fn trace_with_fields(&self, message: &str, fields: &[(&str, &str)])
pub fn trace_with_fields(&self, message: &str, fields: &[(&str, &str)])
Sourcepub fn enter_span(&self, name: &str) -> SpanGuard<Caps>
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 hereSourcepub fn set_request_id(&self, id: impl Into<String>)
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.
Sourcepub fn request_id(&self) -> Option<String>
pub fn request_id(&self) -> Option<String>
Returns the current request correlation ID, if set.
Sourcepub fn log(&self, entry: LogEntry)
pub fn log(&self, entry: LogEntry)
Logs a structured entry to the attached collector, if present.
Sourcepub fn diagnostic_context(&self) -> DiagnosticContext
pub fn diagnostic_context(&self) -> DiagnosticContext
Returns a snapshot of the current diagnostic context.
Sourcepub fn set_diagnostic_context(&self, ctx: DiagnosticContext)
pub fn set_diagnostic_context(&self, ctx: DiagnosticContext)
Replaces the current diagnostic context.
Sourcepub fn set_log_collector(&self, collector: LogCollector)
pub fn set_log_collector(&self, collector: LogCollector)
Attaches a log collector to this context.
Sourcepub fn log_collector(&self) -> Option<LogCollector>
pub fn log_collector(&self) -> Option<LogCollector>
Returns the current log collector, if attached.
Sourcepub fn set_trace_buffer(&self, trace: TraceBufferHandle)
pub fn set_trace_buffer(&self, trace: TraceBufferHandle)
Attaches a trace buffer to this context.
Sourcepub fn trace_buffer(&self) -> Option<TraceBufferHandle>
pub fn trace_buffer(&self) -> Option<TraceBufferHandle>
Returns the current trace buffer handle, if attached.
Sourcepub fn entropy(&self) -> &(dyn EntropySource + 'static)where
Caps: HasRandom,
pub fn entropy(&self) -> &(dyn EntropySource + 'static)where
Caps: HasRandom,
Returns the entropy source for this context.
Sourcepub fn random_u64(&self) -> u64where
Caps: HasRandom,
pub fn random_u64(&self) -> u64where
Caps: HasRandom,
Generates a random u64 using the context entropy source.
Sourcepub fn random_bytes(&self, dest: &mut [u8])where
Caps: HasRandom,
pub fn random_bytes(&self, dest: &mut [u8])where
Caps: HasRandom,
Fills a buffer with random bytes using the context entropy source.
Sourcepub fn random_usize(&self, bound: usize) -> usizewhere
Caps: HasRandom,
pub fn random_usize(&self, bound: usize) -> usizewhere
Caps: HasRandom,
Generates a random usize in [0, bound) with rejection sampling.
Sourcepub fn random_bool(&self) -> boolwhere
Caps: HasRandom,
pub fn random_bool(&self) -> boolwhere
Caps: HasRandom,
Generates a random boolean.
Sourcepub fn random_f64(&self) -> f64where
Caps: HasRandom,
pub fn random_f64(&self) -> f64where
Caps: HasRandom,
Generates a random f64 in [0, 1).
Sourcepub fn shuffle<T>(&self, slice: &mut [T])where
Caps: HasRandom,
pub fn shuffle<T>(&self, slice: &mut [T])where
Caps: HasRandom,
Shuffles a slice in place using Fisher-Yates.
Sourcepub fn set_cancel_requested(&self, value: bool)
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.
Sourcepub fn cancel_with(&self, kind: CancelKind, message: Option<&'static str>)
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 initiatedmessage- 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().
Sourcepub fn cancel_fast(&self, kind: CancelKind)
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());Sourcepub fn cancel_reason(&self) -> Option<CancelReason>
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);
}Sourcepub fn cancel_chain(&self) -> impl Iterator<Item = CancelReason>
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);Sourcepub fn root_cancel_cause(&self) -> Option<CancelReason>
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);
}Sourcepub fn cancelled_by(&self, kind: CancelKind) -> bool
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));Sourcepub fn any_cause_is(&self, kind: CancelKind) -> bool
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 chainSourcepub fn set_cancel_reason(&self, reason: CancelReason)
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);Sourcepub async fn race<T>(
&self,
futures: Vec<Pin<Box<dyn Future<Output = T> + Send>>>,
) -> Result<T, JoinError>
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.
Sourcepub async fn race_named<T>(
&self,
futures: Vec<(&'static str, Pin<Box<dyn Future<Output = T> + Send>>)>,
) -> Result<T, JoinError>
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”).
Sourcepub async fn race_timeout<T>(
&self,
duration: Duration,
futures: Vec<Pin<Box<dyn Future<Output = T> + Send>>>,
) -> Result<T, JoinError>where
Caps: HasTime,
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”).
Sourcepub 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,
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”).
Sourcepub fn scope(&self) -> Scope<'static>
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.
Sourcepub fn scope_with_budget(&self, budget: Budget) -> Scope<'static>
pub fn scope_with_budget(&self, budget: Budget) -> Scope<'static>
Sourcepub fn scope_with_budget_and_capability_budget(
&self,
budget: Budget,
capability_budget: CapabilityBudget,
requirements: CapabilityBudgetRequirements,
) -> Result<Scope<'static>, CapabilityBudgetRefusal>
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
impl Cx
Sourcepub async fn race_drained<T>(
&self,
futures: Vec<Pin<Box<dyn Future<Output = T> + Send>>>,
) -> Result<T, JoinError>where
T: Send + 'static,
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.
Sourcepub 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,
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.
Sourcepub 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,
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).
Sourcepub 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,
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>>
impl Cx<CapSet<false, false, false, false, false>>
Sourcepub fn detached_cancel_context() -> Cx<CapSet<false, false, false, false, false>>
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>
impl<Caps> Cx<Caps>
Sourcepub fn spawn<F, Fut>(
&self,
f: F,
) -> Result<TaskHandle<<Fut as Future>::Output>, SpawnError>
pub fn spawn<F, Fut>( &self, f: F, ) -> Result<TaskHandle<<Fut as Future>::Output>, SpawnError>
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.
Sourcepub fn spawn_in<F, Fut, P>(
&self,
scope: &Scope<'_, P>,
f: F,
) -> Result<TaskHandle<<Fut as Future>::Output>, SpawnError>
pub fn spawn_in<F, Fut, P>( &self, scope: &Scope<'_, P>, f: F, ) -> Result<TaskHandle<<Fut as Future>::Output>, SpawnError>
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.
Sourcepub fn spawn_registered_in<F, Fut, P>(
&self,
scope: &Scope<'_, P>,
f: F,
) -> Result<TaskHandle<<Fut as Future>::Output>, SpawnError>
pub fn spawn_registered_in<F, Fut, P>( &self, scope: &Scope<'_, P>, f: F, ) -> Result<TaskHandle<<Fut as Future>::Output>, SpawnError>
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.
Sourcepub fn spawn_blocking<F, R>(&self, f: F) -> Result<TaskHandle<R>, SpawnError>
pub fn spawn_blocking<F, R>(&self, f: F) -> Result<TaskHandle<R>, SpawnError>
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.
Sourcepub fn spawn_blocking_in<F, R, P>(
&self,
scope: &Scope<'_, P>,
f: F,
) -> Result<TaskHandle<R>, SpawnError>
pub fn spawn_blocking_in<F, R, P>( &self, scope: &Scope<'_, P>, f: F, ) -> Result<TaskHandle<R>, SpawnError>
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.
Sourcepub fn spawn_local<F, Fut>(
&self,
f: F,
) -> Result<TaskHandle<<Fut as Future>::Output>, SpawnError>
pub fn spawn_local<F, Fut>( &self, f: F, ) -> Result<TaskHandle<<Fut as Future>::Output>, SpawnError>
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.
Sourcepub fn spawn_local_in<F, Fut, P>(
&self,
scope: &Scope<'_, P>,
f: F,
) -> Result<TaskHandle<<Fut as Future>::Output>, SpawnError>
pub fn spawn_local_in<F, Fut, P>( &self, scope: &Scope<'_, P>, f: F, ) -> Result<TaskHandle<<Fut as Future>::Output>, SpawnError>
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
impl Cx
Sourcepub fn for_testing() -> Cx
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.
Sourcepub fn for_testing_with_budget(budget: Budget) -> Cx
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).
Sourcepub fn for_testing_with_io() -> Cx
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).
Sourcepub fn for_request_with_budget(budget: Budget) -> Cx
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.
Sourcepub fn for_request() -> Cx
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.
Sourcepub fn for_testing_with_remote(cap: RemoteCap) -> Cx
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>
impl<Caps> Cx<Caps>
Sourcepub fn scoped_cpu<'env, F, R>(
&'env self,
worker_cap: usize,
f: F,
) -> Result<R, ScopedCpuError>
pub fn scoped_cpu<'env, F, R>( &'env self, worker_cap: usize, f: F, ) -> Result<R, ScopedCpuError>
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
ScopedCpuError::Cancelledwhen cancellation or budget exhaustion is observed at entry or at exit (children that observed it mid-run have already drained by then).ScopedCpuError::ChildPanickedwhen a child panicked (first payload; siblings drained; all joined).