ReviewOS

also looking at this

home-lang/home

fix(async): preserve cancellation and add revision-aware job gate

#914
Merged chris wants to merge feat/camera-jobs into main
6 files +217 -20
packages/async/src/completion_gate.zigadded+57-0
Changes to packages/async/src/completion_gate.zig
@@ -0,0 +1,57 @@
1//! Revision-aware two-phase job completion. Small allocation-free adaptation of
2//! Home task states for event-driven jobs; caller owns synchronization.
3//! A terminal outcome is immutable, including under cancellation/late events.
4const std = ;
5pub const State = enum(u32) { pending = 1, active = 2, succeeded = 3, failed = 4, cancelled = 5, stopped = 6 };
6pub const Gate = struct {
7 revision: u64,
8 state: State = .pending,
9 pub fn terminal(self: Gate) bool {
10 return switch (self.state) {
11 .pending, .active => false,
12 else => true,
13 };
14 }
15 pub fn observe(self: *Gate, revision: u64, active: bool, outcome: ?bool) void {
16 if (self.terminal() or revision < self.revision) return;
17 if (active) self.state = .active;
18 if (self.state == .active) {
19 if (outcome) |success| {
20 self.state = if (success) .succeeded else .failed;
21 }
22 }
23 }
24 pub fn cancel(self: *Gate) void {
25 if (!self.terminal()) self.state = .cancelled;
26 }
27 pub fn stop(self: *Gate) void {
28 if (!self.terminal()) self.state = .stopped;
29 }
30};
31test "gate ignores stale and pre-active results; cancellation is terminal" {
32 var g: Gate = .{ .revision = 9 };
33 g.observe(8, true, null);
34 g.observe(9, false, true);
35 try std.testing.expectEqual(State.pending, g.state);
36 g.observe(9, true, null);
37 try std.testing.expectEqual(State.active, g.state);
38 g.cancel();
39 g.observe(10, false, true);
40 try std.testing.expectEqual(State.cancelled, g.state);
41}
42test "success failure and stop never overwrite a terminal result" {
43 var g: Gate = .{ .revision = 1 };
44 g.observe(1, true, null);
45 g.observe(2, false, true);
46 g.cancel();
47 g.stop();
48 try std.testing.expectEqual(State.succeeded, g.state);
49 g = .{ .revision = 1 };
50 g.observe(1, true, null);
51 g.observe(1, false, false);
52 try std.testing.expectEqual(State.failed, g.state);
53 g = .{ .revision = 1 };
54 g.stop();
55 g.observe(1, true, true);
56 try std.testing.expectEqual(State.stopped, g.state);
57}