Owner-channel errors
An error escaping a child belongs to the owner's ordinary exn channel. It does not wait inside the task for somebody to remember another flavor of await. The owner interrupts siblings, drains cleanup, then propagates the structural tag.
type FailureProbe = {
mut events: Vector(String);
}
let recordrecord: fn(probe: Book.Concurrency.Failure.FailureProbe @ global, event: String @ global) -> Unit = fn(probeprobe: Book.Concurrency.Failure.FailureProbe, eventevent: String) {
probe.events := Vector.pushpush: pure fn!('a)(items: Vector(String), item: String) -> Vector(String)(probeprobe: Book.Concurrency.Failure.FailureProbe.eventsevents: Vector(String), eventevent: String);
};
let fail_with_siblingfail_with_sibling: fn(probe: Book.Concurrency.Failure.FailureProbe @ global) -> Int, exn [ :child_failed(String) ] = fn(probeprobe: Book.Concurrency.Failure.FailureProbe: FailureProbe @ global) {
let (~value: sibling_startedsibling_started: OneShot(Unit), ~resolver: start_siblingstart_sibling: OneShot.Resolver(Unit)) = OneShot.makemake: pure fn!('a)() -> (value: OneShot(Unit), resolver: OneShot.Resolver(Unit) @ unique)();
let (~value: parkedparked: OneShot(Unit), ..) = OneShot.makemake: pure fn!('a)() -> (value: OneShot(Unit), resolver: OneShot.Resolver(Unit) @ unique)();
let _sibling_sibling: Async.Task(Unit) = spawn {
defer recordrecord: fn(probe: Book.Concurrency.Failure.FailureProbe @ global, event: String @ global) -> Unit(probeprobe: Book.Concurrency.Failure.FailureProbe, "sibling cleaned""sibling cleaned": String);
OneShot.resolveresolve: pure fn!('a)(resolver: OneShot.Resolver(Unit) @ unique, value: Unit @ global) -> Unit(start_siblingstart_sibling: OneShot.Resolver(Unit), ());
await parkedparked: OneShot(Unit);
};
await sibling_startedsibling_started: OneShot(Unit);
let _failed_failed: Async.Task('a) = spawn {
error :child_failed("boom""boom": String);
};
await parkedparked: OneShot(Unit);
00: Int
};
Child Failure Fails Owner
let probeprobe: Book.Concurrency.Failure.FailureProbe: FailureProbe @ global = FailureProbe { events = []; };
expect ✓try fail_with_siblingfail_with_sibling: fn(probe: Book.Concurrency.Failure.FailureProbe @ global) -> Int, exn [ :child_failed(String) ](probeprobe: Book.Concurrency.Failure.FailureProbe) {
| :child_failed("boom") -> 4242: Int
} == 4242: Int;
expect ✓probeprobe: Book.Concurrency.Failure.FailureProbe.eventsevents: Vector(String) == ["sibling cleaned""sibling cleaned": String];Expected recovery
Recover inside a child when its failure is part of that child's ordinary result. Only errors that escape the child fail the concurrent call.
let find_countfind_count: fn(found: Bool) -> Int, exn [ :missing ] = fn(foundfound: Bool) {
if foundfound: Bool == FalseFalse: Bool {
error :missing;
}
33: Int
};
Child Handles Expected Error
let tasktask: Async.Task(Int) = spawn {
try find_countfind_count: fn(found: Bool) -> Int, exn [ :missing ](FalseFalse: Bool) {
| :missing -> 00: Int
}
};
expect ✓await tasktask: Async.Task(Int) == 00: Int;