authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-19 15:03:03-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-20 15:08:59-07:00
log4e621d4260ed752995dedc50a240931fc0e0941f
treea01b071b03680b3e1bf57bf73cdd8f47c625ce03
parente00b6db2aa2e3aa7e61c4b1ccebf4ebdb6e2d45a

workaround for std lib AutoResetEvent bug


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).
13pub const AutoResetEvent = struct {13pub const AutoResetEvent = struct {
14 // AutoResetEvent has 3 possible states:14 /// AutoResetEvent has 3 possible states:
15 // - UNSET: the AutoResetEvent is currently unset15 /// - UNSET: the AutoResetEvent is currently unset
16 // - SET: the AutoResetEvent was notified before a wait() was called16 /// - 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 set20 /// 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 event24 /// if the event is unset, then we set the event
25 // if theres a waiting ResetEvent, then we unset the event and notify the ResetEvent25 /// 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 issued27 /// 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 229 /// 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,
3636
37 const UNSET = 0;37 const UNSET = 0;
38 const SET = 1;38 const SET = 1;
3939
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);
4242
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.
6const std = @import("std");
7const Event = @This();
8
9lock: std.Mutex = .{},
10event: std.ResetEvent = undefined,
11state: enum { empty, waiting, notified } = .empty,
12
13pub 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
29pub 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.
1const std = @import("std");6const std = @import("std");
2const ThreadPool = @This();7const ThreadPool = @This();
38
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.
1const std = @import("std");6const std = @import("std");
2const WaitGroup = @This();7const WaitGroup = @This();
8const Event = @import("Event.zig");
39
4lock: std.Mutex = .{},10lock: std.Mutex = .{},
5counter: usize = 0,11counter: usize = 0,
6event: std.AutoResetEvent = .{},12event: Event = .{},
713
8pub fn start(self: *WaitGroup) void {14pub 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}
2329
24pub fn wait(self: *WaitGroup) void {30pub 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();
2835
29 if (self.counter == 0)36 if (self.counter == 0)
30 return;37 return;
31 }38 }
3239
33 self.event.wait();40 self.event.wait();
41 }
34}42}