| ... | ... | @@ -7,14 +7,15 @@ const std = @import("std.zig"); |
| 7 | 7 | const builtin = @import("builtin"); |
| 8 | 8 | const testing = std.testing; |
| 9 | 9 | const assert = std.debug.assert; |
| 10 | const StaticResetEvent = std.StaticResetEvent; |
| 10 | 11 | |
| 11 | | /// Similar to std.ResetEvent but on `set()` it also (atomically) does `reset()`. |
| 12 | | /// Unlike std.ResetEvent, `wait()` can only be called by one thread (MPSC-like). |
| 12 | /// Similar to `StaticResetEvent` but on `set()` it also (atomically) does `reset()`. |
| 13 | /// Unlike StaticResetEvent, `wait()` can only be called by one thread (MPSC-like). |
| 13 | 14 | pub const AutoResetEvent = struct { |
| 14 | 15 | /// AutoResetEvent has 3 possible states: |
| 15 | 16 | /// - UNSET: the AutoResetEvent is currently unset |
| 16 | 17 | /// - SET: the AutoResetEvent was notified before a wait() was called |
| 17 | | /// - <std.ResetEvent pointer>: there is an active waiter waiting for a notification. |
| 18 | /// - <StaticResetEvent pointer>: there is an active waiter waiting for a notification. |
| 18 | 19 | /// |
| 19 | 20 | /// When attempting to wait: |
| 20 | 21 | /// if the event is unset, it registers a ResetEvent pointer to be notified when the event is set |
| ... | ... | @@ -25,20 +26,20 @@ pub const AutoResetEvent = struct { |
| 25 | 26 | /// if theres a waiting ResetEvent, then we unset the event and notify the ResetEvent |
| 26 | 27 | /// |
| 27 | 28 | /// This ensures that the event is automatically reset after a wait() has been issued |
| 28 | | /// and avoids the race condition when using std.ResetEvent in the following scenario: |
| 29 | | /// thread 1 | thread 2 |
| 30 | | /// std.ResetEvent.wait() | |
| 31 | | /// | std.ResetEvent.set() |
| 32 | | /// | std.ResetEvent.set() |
| 33 | | /// std.ResetEvent.reset() | |
| 34 | | /// std.ResetEvent.wait() | (missed the second .set() notification above) |
| 29 | /// and avoids the race condition when using StaticResetEvent in the following scenario: |
| 30 | /// thread 1 | thread 2 |
| 31 | /// StaticResetEvent.wait() | |
| 32 | /// | StaticResetEvent.set() |
| 33 | /// | StaticResetEvent.set() |
| 34 | /// StaticResetEvent.reset() | |
| 35 | /// StaticResetEvent.wait() | (missed the second .set() notification above) |
| 35 | 36 | state: usize = UNSET, |
| 36 | 37 | |
| 37 | 38 | const UNSET = 0; |
| 38 | 39 | const SET = 1; |
| 39 | 40 | |
| 40 | | /// the minimum alignment for the `*std.ResetEvent` created by wait*() |
| 41 | | const event_align = std.math.max(@alignOf(std.ResetEvent), 2); |
| 41 | /// the minimum alignment for the `*StaticResetEvent` created by wait*() |
| 42 | const event_align = std.math.max(@alignOf(StaticResetEvent), 2); |
| 42 | 43 | |
| 43 | 44 | pub fn wait(self: *AutoResetEvent) void { |
| 44 | 45 | self.waitFor(null) catch unreachable; |
| ... | ... | @@ -49,12 +50,9 @@ pub const AutoResetEvent = struct { |
| 49 | 50 | } |
| 50 | 51 | |
| 51 | 52 | fn waitFor(self: *AutoResetEvent, timeout: ?u64) error{TimedOut}!void { |
| 52 | | // lazily initialized std.ResetEvent |
| 53 | | var reset_event: std.ResetEvent align(event_align) = undefined; |
| 53 | // lazily initialized StaticResetEvent |
| 54 | var reset_event: StaticResetEvent align(event_align) = undefined; |
| 54 | 55 | var has_reset_event = false; |
| 55 | | defer if (has_reset_event) { |
| 56 | | reset_event.deinit(); |
| 57 | | }; |
| 58 | 56 | |
| 59 | 57 | var state = @atomicLoad(usize, &self.state, .SeqCst); |
| 60 | 58 | while (true) { |
| ... | ... | @@ -72,7 +70,7 @@ pub const AutoResetEvent = struct { |
| 72 | 70 | // lazily initialize the ResetEvent if it hasn't been already |
| 73 | 71 | if (!has_reset_event) { |
| 74 | 72 | has_reset_event = true; |
| 75 | | reset_event = std.ResetEvent.init(); |
| 73 | reset_event = .{}; |
| 76 | 74 | } |
| 77 | 75 | |
| 78 | 76 | // Since the AutoResetEvent currently isnt set, |
| ... | ... | @@ -97,9 +95,10 @@ pub const AutoResetEvent = struct { |
| 97 | 95 | }; |
| 98 | 96 | |
| 99 | 97 | // wait with a timeout and return if signalled via set() |
| 100 | | if (reset_event.timedWait(timeout_ns)) |_| { |
| 101 | | return; |
| 102 | | } else |timed_out| {} |
| 98 | switch (reset_event.timedWait(timeout_ns)) { |
| 99 | .event_set => return, |
| 100 | .timed_out => {}, |
| 101 | } |
| 103 | 102 | |
| 104 | 103 | // If we timed out, we need to transition the AutoResetEvent back to UNSET. |
| 105 | 104 | // If we don't, then when we return, a set() thread could observe a pointer to an invalid ResetEvent. |
| ... | ... | @@ -164,7 +163,7 @@ pub const AutoResetEvent = struct { |
| 164 | 163 | continue; |
| 165 | 164 | } |
| 166 | 165 | |
| 167 | | const reset_event = @intToPtr(*align(event_align) std.ResetEvent, state); |
| 166 | const reset_event = @intToPtr(*align(event_align) StaticResetEvent, state); |
| 168 | 167 | reset_event.set(); |
| 169 | 168 | return; |
| 170 | 169 | } |