Structured concurrency
concurrency/main.xo
fn square(n: Int) -> Int { n * n }
fn main(os: Os) {
let sum = scope s {
let a = s.spawn(fn() { square(3) })
let b = s.spawn(fn() { square(4) })
a.await + b.await
}
os.stdio.println("sum ${sum}")
let ch = Chan[Int].new(cap: 8)
let total = scope s {
s.spawn(fn() {
for i in 1..=5 { ch.send(i) }
ch.close()
})
var t = 0
for x in ch { t += x }
t
}
os.stdio.println("total ${total}")
}Output:
sum 25
total 15
scope s { ... }is an expression.s.spawn(f)starts a task inside it, andt.awaitwaits for its result.- No task outlives its scope. When the body finishes, the scope waits for its tasks; when it fails or returns early, it cancels them and waits for them to unwind. There are no leaked goroutines to find later.
- If a task fails, the scope cancels its siblings and the error propagates
like
?. - Channels work as in Go:
for x in chreceives until the channel is closed and drained, andselectwaits on several at once. - Deadlines and cancellation flow down the scope tree, so there is no
ctxparameter to thread through every call. xo testruns tasks on a deterministic scheduler, andxo test --explore=Nsearches other interleavings for a failing one.