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 };