Use concurrent when independent expressions run concurrently and the caller requires a single named result record. Standard application code should select the bounded form with an explicit timeout fallback.
Complete successfully
Save this as concurrency-success.tpz:
let result = concurrent(timeout: 3s) {
total: 1 + 2
label: "ready"
} else {
{ total: -1, label: "timeout" }
}
print("{result.total} {result.label}")Check and run it:
topaz check concurrency-success.tpz
topaz run concurrency-success.tpzBoth arms finish before the deadline, so their names become record fields:
3 readyThe 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:
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:
topaz check concurrency-timeout.tpz
topaz run concurrency-timeout.tpzThe deadline wins, no partial arm record is exposed, and the fallback supplies the entire result:
-1Distinguish the three outcomes
| What happens before the deadline? | Expression result |
|---|---|
| Every arm completes | A record with one field per arm |
| The timeout wins | The else block value |
| An arm faults | The 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.
What the names do not guarantee
Arm names fix result association, not scheduling. Topaz does not promise arm start order, completion order, output interleaving, fairness, or deterministic interleaving. Do not put print calls in arms when their order matters. Join first, then print the record as the examples do.
When a timeout abandons work, cancellation and cleanup are runtime policy. Later faults from abandoned work are not observed by the Topaz expression. This is not permission to rely on a particular scheduler or on portable cleanup after timeout.
Common corrections
- Write
3sor5mswith no space. Property forms such as.secondsare not duration syntax. - A timeout form requires
else. A plain join cannot haveelse. elsehandles timeout only. It does not catch a fault or unwrap anErr.- 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.