| 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,33 +11,33 @@ const assert = std.debug.assert; |
| 11 | /// Similar to std.ResetEvent but on `set()` it also (atomically) does `reset()`. | 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 | /// Unlike std.ResetEvent, `wait()` can only be called by one thread (MPSC-like). |
| 13 | pub const AutoResetEvent = struct { | 13 | pub const AutoResetEvent = struct { |
| 14 | // AutoResetEvent has 3 possible states: | 14 | /// AutoResetEvent has 3 possible states: |
| 15 | // - UNSET: the AutoResetEvent is currently unset | 15 | /// - UNSET: the AutoResetEvent is currently unset |
| 16 | // - SET: the AutoResetEvent was notified before a wait() was called | 16 | /// - SET: the AutoResetEvent was notified before a wait() was called |
| 17 | // - <std.ResetEvent pointer>: there is an active waiter waiting for a notification. | 17 | /// - <std.ResetEvent pointer>: there is an active waiter waiting for a notification. |
| 18 | // | 18 | /// |
| 19 | // When attempting to wait: | 19 | /// When attempting to wait: |
| 20 | // if the event is unset, it registers a ResetEvent pointer to be notified when the event is set | 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. | 21 | /// if the event is already set, then it consumes the notification and resets the event. |
| 22 | // | 22 | /// |
| 23 | // When attempting to notify: | 23 | /// When attempting to notify: |
| 24 | // if the event is unset, then we set the event | 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 | 25 | /// if theres a waiting ResetEvent, then we unset the event and notify the ResetEvent |
| 26 | // | 26 | /// |
| 27 | // This ensures that the event is automatically reset after a wait() has been issued | 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: | 28 | /// and avoids the race condition when using std.ResetEvent in the following scenario: |
| 29 | // thread 1 | thread 2 | 29 | /// thread 1 | thread 2 |
| 30 | // std.ResetEvent.wait() | | 30 | /// std.ResetEvent.wait() | |
| 31 | // | std.ResetEvent.set() | 31 | /// | std.ResetEvent.set() |
| 32 | // | std.ResetEvent.set() | 32 | /// | std.ResetEvent.set() |
| 33 | // std.ResetEvent.reset() | | 33 | /// std.ResetEvent.reset() | |
| 34 | // std.ResetEvent.wait() | (missed the second .set() notification above) | 34 | /// std.ResetEvent.wait() | (missed the second .set() notification above) |
| 35 | state: usize = UNSET, | 35 | state: usize = UNSET, |
| 36 | 36 | ||
| 37 | const UNSET = 0; | 37 | const UNSET = 0; |
| 38 | const SET = 1; | 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 | const event_align = std.math.max(@alignOf(std.ResetEvent), 2); | 41 | const event_align = std.math.max(@alignOf(std.ResetEvent), 2); |
| 42 | 42 | ||
| 43 | pub fn wait(self: *AutoResetEvent) void { | 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,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 | const std = @import("std"); | 6 | const std = @import("std"); |
| 2 | const ThreadPool = @This(); | 7 | const ThreadPool = @This(); |
| 3 | 8 |
src/WaitGroup.zig+16-8| ... | @@ -1,9 +1,15 @@ | ... | @@ -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 | const std = @import("std"); | 6 | const std = @import("std"); |
| 2 | const WaitGroup = @This(); | 7 | const WaitGroup = @This(); |
| 8 | const Event = @import("Event.zig"); | ||
| 3 | 9 | ||
| 4 | lock: std.Mutex = .{}, | 10 | lock: std.Mutex = .{}, |
| 5 | counter: usize = 0, | 11 | counter: usize = 0, |
| 6 | event: std.AutoResetEvent = .{}, | 12 | event: Event = .{}, |
| 7 | 13 | ||
| 8 | pub fn start(self: *WaitGroup) void { | 14 | pub fn start(self: *WaitGroup) void { |
| 9 | const held = self.lock.acquire(); | 15 | const held = self.lock.acquire(); |
| ... | @@ -22,13 +28,15 @@ pub fn stop(self: *WaitGroup) void { | ... | @@ -22,13 +28,15 @@ pub fn stop(self: *WaitGroup) void { |
| 22 | } | 28 | } |
| 23 | 29 | ||
| 24 | pub fn wait(self: *WaitGroup) void { | 30 | pub fn wait(self: *WaitGroup) void { |
| 25 | { | 31 | while (true) { |
| 26 | const held = self.lock.acquire(); | 32 | { |
| 27 | defer held.release(); | 33 | const held = self.lock.acquire(); |
| 34 | defer held.release(); | ||
| 28 | 35 | ||
| 29 | if (self.counter == 0) | 36 | if (self.counter == 0) |
| 30 | return; | 37 | return; |
| 31 | } | 38 | } |
| 32 | 39 | ||
| 33 | self.event.wait(); | 40 | self.event.wait(); |
| 41 | } | ||
| 34 | } | 42 | } |