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:
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.
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
3sor5mswith no space. Property forms such as.secondsare not duration syntax. - A timeout duration scaled to milliseconds must fit unsigned
u64(0..=18446744073709551615), with exact maxima18446744073709551615ms,18446744073709551s, and307445734561825m; larger values are rejected withTPZ5001. - 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.