| author | |
| committer | |
| log | 4e621d4260ed752995dedc50a240931fc0e0941f |
| tree | a01b071b03680b3e1bf57bf73cdd8f47c625ce03 |
| parent | e00b6db2aa2e3aa7e61c4b1ccebf4ebdb6e2d45a |
4 files changed, 86 insertions(+), 30 deletions(-)
lib/std/auto_reset_event.zig+22-22| ... | ... | @@ -11,33 +11,33 @@ const assert = std.debug.assert; |
| 11 | 11 | /// Similar to std.ResetEvent but on `set()` it also (atomically) does `reset()`. |
| 12 | 12 | /// Unlike std.ResetEvent, `wait()` can only be called by one thread (MPSC-like). |
| 13 | 13 | pub const AutoResetEvent = struct { |
| 14 | // AutoResetEvent has 3 possible states: | |
| 15 | // - UNSET: the AutoResetEvent is currently unset | |
| 16 | // - SET: the AutoResetEvent was notified before a wait() was called | |
| 17 | // - <std.ResetEvent pointer>: there is an active waiter waiting for a notification. | |
| 18 | // | |
| 19 | // When attempting to wait: | |
| 20 | // if the event is unset, it registers a ResetEvent pointer to be notified when the event is set | |
| 21 | // if the event is already set, then it consumes the notification and resets the event. | |
| 22 | // | |
| 23 | // When attempting to notify: | |
| 24 | // if the event is unset, then we set the event | |
| 25 | // if theres a waiting ResetEvent, then we unset the event and notify the ResetEvent | |
| 26 | // | |
| 27 | // 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) | |
| 14 | /// AutoResetEvent has 3 possible states: | |
| 15 | /// - UNSET: the AutoResetEvent is currently unset | |
| 16 | /// - SET: the AutoResetEvent was notified before a wait() was called | |
| 17 | /// - <std.ResetEvent pointer>: there is an active waiter waiting for a notification. | |
| 18 | /// | |
| 19 | /// When attempting to wait: | |
| 20 | /// if the event is unset, it registers a ResetEvent pointer to be notified when the event is set | |
| 21 | /// if the event is already set, then it consumes the notification and resets the event. | |
| 22 | /// | |
| 23 | /// When attempting to notify: | |
| 24 | /// if the event is unset, then we set the event | |
| 25 | /// if theres a waiting ResetEvent, then we unset the event and notify the ResetEvent | |
| 26 | /// | |
| 27 | /// 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) | |
| 35 | 35 | state: usize = UNSET, |
| 36 | 36 | |
| 37 | 37 | const UNSET = 0; |
| 38 | 38 | const SET = 1; |
| 39 | 39 | |
| 40 | // the minimum alignment for the `*std.ResetEvent` created by wait*() | |
| 40 | /// the minimum alignment for the `*std.ResetEvent` created by wait*() | |
| 41 | 41 | const event_align = std.math.max(@alignOf(std.ResetEvent), 2); |
| 42 | 42 | |
| 43 | 43 | pub fn wait(self: *AutoResetEvent) void { |
src/Event.zig created+43| ... | ... | @@ -0,0 +1,43 @@ |
| 1 | // SPDX-License-Identifier: MIT | |
| 2 | // Copyright (c) 2015-2020 Zig Contributors | |
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | |
| 4 | // The MIT license requires this copyright notice to be included in all copies | |
| 5 | // and substantial portions of the software. | |
| 6 | const std = @import("std"); | |
| 7 | const Event = @This(); | |
| 8 | ||
| 9 | lock: std.Mutex = .{}, | |
| 10 | event: std.ResetEvent = undefined, | |
| 11 | state: enum { empty, waiting, notified } = .empty, | |
| 12 | ||
| 13 | pub fn wait(self: *Event) void { | |
| 14 | const held = self.lock.acquire(); | |
| 15 | ||
| 16 | switch (self.state) { | |
| 17 | .empty => { | |
| 18 | self.state = .waiting; | |
| 19 | self.event = @TypeOf(self.event).init(); | |
| 20 | held.release(); | |
| 21 | self.event.wait(); | |
| 22 | self.event.deinit(); | |
| 23 | }, | |
| 24 | .waiting => unreachable, | |
| 25 | .notified => held.release(), | |
| 26 | } | |
| 27 | } | |
| 28 | ||
| 29 | pub fn set(self: *Event) void { | |
| 30 | const held = self.lock.acquire(); | |
| 31 | ||
| 32 | switch (self.state) { | |
| 33 | .empty => { | |
| 34 | self.state = .notified; | |
| 35 | held.release(); | |
| 36 | }, | |
| 37 | .waiting => { | |
| 38 | held.release(); | |
| 39 | self.event.set(); | |
| 40 | }, | |
| 41 | .notified => unreachable, | |
| 42 | } | |
| 43 | } |
src/ThreadPool.zig+5| ... | ... | @@ -1,3 +1,8 @@ |
| 1 | // SPDX-License-Identifier: MIT | |
| 2 | // Copyright (c) 2015-2020 Zig Contributors | |
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | |
| 4 | // The MIT license requires this copyright notice to be included in all copies | |
| 5 | // and substantial portions of the software. | |
| 1 | 6 | const std = @import("std"); |
| 2 | 7 | const ThreadPool = @This(); |
| 3 | 8 |
src/WaitGroup.zig+16-8| ... | ... | @@ -1,9 +1,15 @@ |
| 1 | // SPDX-License-Identifier: MIT | |
| 2 | // Copyright (c) 2015-2020 Zig Contributors | |
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | |
| 4 | // The MIT license requires this copyright notice to be included in all copies | |
| 5 | // and substantial portions of the software. | |
| 1 | 6 | const std = @import("std"); |
| 2 | 7 | const WaitGroup = @This(); |
| 8 | const Event = @import("Event.zig"); | |
| 3 | 9 | |
| 4 | 10 | lock: std.Mutex = .{}, |
| 5 | 11 | counter: usize = 0, |
| 6 | event: std.AutoResetEvent = .{}, | |
| 12 | event: Event = .{}, | |
| 7 | 13 | |
| 8 | 14 | pub fn start(self: *WaitGroup) void { |
| 9 | 15 | const held = self.lock.acquire(); |
| ... | ... | @@ -22,13 +28,15 @@ pub fn stop(self: *WaitGroup) void { |
| 22 | 28 | } |
| 23 | 29 | |
| 24 | 30 | pub fn wait(self: *WaitGroup) void { |
| 25 | { | |
| 26 | const held = self.lock.acquire(); | |
| 27 | defer held.release(); | |
| 31 | while (true) { | |
| 32 | { | |
| 33 | const held = self.lock.acquire(); | |
| 34 | defer held.release(); | |
| 28 | 35 | |
| 29 | if (self.counter == 0) | |
| 30 | return; | |
| 31 | } | |
| 36 | if (self.counter == 0) | |
| 37 | return; | |
| 38 | } | |
| 32 | 39 | |
| 33 | self.event.wait(); | |
| 40 | self.event.wait(); | |
| 41 | } | |
| 34 | 42 | } |