Skip to content

Tasks

Join and inspect the typed handle returned by spawn.

Updated View as Markdown

Task[T] is the typed handle for a spawned computation that will produce T. Application code normally obtains one from spawn.

task: Task[int] = spawn { calculate_total() }

The handle carries the result type but does not contain the result immediately. The child owns its execution frame until it completes and the parent consumes that result.

Joining

Call .await() to wait for completion and retrieve the result:

value: int = task.await()

await is a method, not a keyword. It has three important properties:

  1. it may suspend the caller while the child is running;
  2. it returns the child’s T;
  3. it consumes and reaps the completed task handle.

Treat a task as single-consumer. Do not await it twice, put the same task in multiple select arms, or use it after a selection consumed it.

If the child already completed, await() takes a synchronous fast path. The method still has a static Suspend effect because the caller cannot generally know completion state.

Wide results are copied out before the child’s frame is reaped. That transport detail is invisible to source code; returning a struct, tuple, list, or string uses the same Task[T] contract as returning an integer.

Completion order

Joining tasks in source order does not make their work sequential if all tasks were spawned first:

left := spawn { fetch_left() }
right := spawn { fetch_right() }

left_value := left.await()
right_value := right.await()

Both children are eligible to run before the first join completes. The joins only choose the order in which the parent retrieves their values.

By contrast, this starts the second call only after the first finishes:

left_value := (spawn { fetch_left() }).await()
right_value := (spawn { fetch_right() }).await()

Spawn all independent work first when overlap is intended.

Result channels

Task[T] preserves whatever type the body returns. A child whose ordinary value is Result[Record, LoadError] produces Task[Result[Record, LoadError]]; awaiting returns that result for normal matching or ? propagation.

Task cancellation is not encoded as an automatic Err member of T. Define an application error only when cancellation itself belongs to that API’s domain contract.

Raw handles

Task[T].raw_handle() exposes the scheduler handle as usize. It exists for the compiler’s selectable protocol and low-level runtime integration:

handle: usize = task.raw_handle()

Ordinary application code should retain the typed task instead. A raw handle does not transfer the type, result ownership, or reaping rules.

Do not persist, serialize, compare for business identity, or reconstruct Task[T] from a raw handle. It is an arena-relative scheduler token whose encoding belongs to the runtime ABI.

Available methods

The current built-in surface is intentionally small:

Method Result Purpose
await() T Join, retrieve, and reap
cancel() void Request cooperative cancellation
raw_handle() usize Runtime/select integration

There is no stable is_complete, timeout, detach, or multi-await API on Task[T] today. Build timeouts by racing a task against an appropriate timer source once that source is available in the target runtime.

Ownership

Dropping the source-level handle does not detach the child from the parent task tree. The runtime can still discover it for subtree cancellation and cleanup. However, without a handle the parent cannot retrieve its result or request targeted cancellation.

Keep the handle when completion, errors, or shutdown matter. Discard it only for deliberately best-effort child work with an explicit failure policy.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close