Use concurrent when independent expressions may run together and the caller
needs one named result record. Ordinary application code should choose 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.tpz
Both arms finish before the deadline, so their names become record fields:
3 readyThe record associates each value with its arm name. This does not reveal 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.tpz
The deadline wins, no partial arm record is exposed, and the fallback supplies the whole 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; else does not run |
An arm that evaluates 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 the same 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.