Program Structure

Concurrency

Execute named work with a declared timeout, then distinguish success, timeout, Result values, and faults.

Use concurrent when independent expressions run concurrently and the caller requires a single named result record. Standard application code declares a duration and an explicit timeout fallback.

Complete successfully

Save this as concurrency-success.tpz:

TOPAZ
let result = concurrent(timeout: 3s) {
    total: 1 + 2
    label: "ready"
} else {
    { total: -1, label: "timeout" }
}

print("{result.total} {result.label}")

Check and run it:

BASH
topaz check concurrency-success.tpz
topaz run concurrency-success.tpz

Both arms finish before the deadline, so their names become record fields:

Output
3 ready

The record associates each value with its arm name. This does not indicate which arm started or completed first.

Let the deadline choose the fallback

This second program has an arm that cannot finish. Save it as concurrency-timeout.tpz:

TOPAZ
function spin() -> int {
    let mut i = 0
    while true { i += 1 }
    i
}

let result = concurrent(timeout: 5ms) {
    stuck: spin()
} else {
    { stuck: -1 }
}

print("{result.stuck}")

Check and run it:

BASH
topaz check concurrency-timeout.tpz
topaz run concurrency-timeout.tpz

The deadline wins, no partial arm record is exposed, and the fallback supplies the entire result:

Output
-1

Distinguish the three outcomes

What happens before the deadline?Expression result
Every arm completesA record with one field per arm
The timeout winsThe else block value
An arm faultsThe whole expression faults, and else does not run

An arm evaluating to Err(error) has not faulted. That Result is an ordinary value stored in its named record field. The fallback must be type-compatible with the successful arm record, which is why each example returns identical field names and compatible value types on both paths.

Result names and scheduling

Arm names map results to record fields. Scheduler start order, completion order, output interleaving, fairness, and deterministic interleaving are unspecified. When output order matters, join the tasks first and then print the record.

After a timeout, runtime policy handles cancellation and resource cleanup. Later faults from abandoned work are not delivered to the Topaz expression. Code should remain independent of scheduler choice and post-timeout cleanup details.

Common corrections

  • Write 3s or 5ms with no space. Property forms such as .seconds are not duration syntax.
  • A timeout duration scaled to milliseconds must fit unsigned u64 (0..=18446744073709551615), with exact maxima 18446744073709551615ms, 18446744073709551s, and 307445734561825m; larger values are rejected with TPZ5001.
  • A timeout form requires else. A plain join cannot have else.
  • else handles timeout only. It does not catch a fault or unwrap an Err.
  • Arm names must be unique, and no syntax exposes a partial result.

The plain concurrent { ... } join is useful on an advanced concurrency page when waiting without a timeout is itself the point. It is not the default form for ordinary examples.

There is no implicit async model, async/await syntax, public task handle, general duration arithmetic, or specified cancellation protocol. See Runtime Behavior for observable fault and order boundaries and Forbidden & Deferred Forms for familiar-looking forms that are not current Topaz.