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;
1111/// Similar to std.ResetEvent but on `set()` it also (atomically) does `reset()`.
1212/// Unlike std.ResetEvent, `wait()` can only be called by one thread (MPSC-like).
1313pub 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)
3535 state: usize = UNSET,
3636
3737 const UNSET = 0;
3838 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*()
4141 const event_align = std.math.max(@alignOf(std.ResetEvent), 2);
4242
4343 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// 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.
16const std = @import("std");
27const ThreadPool = @This();
38
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.
16const std = @import("std");
27const WaitGroup = @This();
8const Event = @import("Event.zig");
39
410lock: std.Mutex = .{},
511counter: usize = 0,
6event: std.AutoResetEvent = .{},
12event: Event = .{},
713
814pub fn start(self: *WaitGroup) void {
915 const held = self.lock.acquire();
......@@ -22,13 +28,15 @@ pub fn stop(self: *WaitGroup) void {
2228}
2329
2430pub 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();
2835
29 if (self.counter == 0)
30 return;
31 }
36 if (self.counter == 0)
37 return;
38 }
3239
33 self.event.wait();
40 self.event.wait();
41 }
3442}