lib/choir/src/composition/loaded/gate.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2
3 const closing_mask: u64 = 1 << 63;
4 const invocation_count_mask: u64 = closing_mask - 1;
5
6 pub const Gate = struct {
7 next_id: std.atomic.Value(u64) = std.atomic.Value(u64).init(1),
8 state: std.atomic.Value(u64) = std.atomic.Value(u64).init(0),
9
10 pub fn reserve(self: *Gate) ?u64 {
11 var state = self.state.load(.acquire);
12 while (true) {
13 if (state & closing_mask != 0 or state & invocation_count_mask == invocation_count_mask) return null;
14 if (self.state.cmpxchgWeak(state, state + 1, .acq_rel, .acquire)) |observed| {
15 state = observed;
16 continue;
17 }
18 break;
19 }
20 return self.next_id.fetchAdd(1, .monotonic);
21 }
22
23 pub fn release(self: *Gate) void {
24 const previous_state = self.state.fetchSub(1, .acq_rel);
25 if (previous_state & invocation_count_mask == 0) @panic("released inactive composition invocation");
26 }
27
28 pub fn close(self: *Gate) void {
29 const previous_state = self.state.fetchOr(closing_mask, .acq_rel);
30 if (previous_state & invocation_count_mask != 0) @panic("composition destroyed with active invocations");
31 }
32
33 pub fn activeCount(self: *const Gate) u64 {
34 return self.state.load(.acquire) & invocation_count_mask;
35 }
36
37 pub fn isClosing(self: *const Gate) bool {
38 return self.state.load(.acquire) & closing_mask != 0;
39 }
40 };