mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
workaround for std lib AutoResetEvent bug
This commit is contained in:
parent
e00b6db2aa
commit
4e621d4260
@ -11,33 +11,33 @@ const assert = std.debug.assert;
|
||||
/// Similar to std.ResetEvent but on `set()` it also (atomically) does `reset()`.
|
||||
/// Unlike std.ResetEvent, `wait()` can only be called by one thread (MPSC-like).
|
||||
pub const AutoResetEvent = struct {
|
||||
// AutoResetEvent has 3 possible states:
|
||||
// - UNSET: the AutoResetEvent is currently unset
|
||||
// - SET: the AutoResetEvent was notified before a wait() was called
|
||||
// - <std.ResetEvent pointer>: there is an active waiter waiting for a notification.
|
||||
//
|
||||
// When attempting to wait:
|
||||
// if the event is unset, it registers a ResetEvent pointer to be notified when the event is set
|
||||
// if the event is already set, then it consumes the notification and resets the event.
|
||||
//
|
||||
// When attempting to notify:
|
||||
// if the event is unset, then we set the event
|
||||
// if theres a waiting ResetEvent, then we unset the event and notify the ResetEvent
|
||||
//
|
||||
// This ensures that the event is automatically reset after a wait() has been issued
|
||||
// and avoids the race condition when using std.ResetEvent in the following scenario:
|
||||
// thread 1 | thread 2
|
||||
// std.ResetEvent.wait() |
|
||||
// | std.ResetEvent.set()
|
||||
// | std.ResetEvent.set()
|
||||
// std.ResetEvent.reset() |
|
||||
// std.ResetEvent.wait() | (missed the second .set() notification above)
|
||||
/// AutoResetEvent has 3 possible states:
|
||||
/// - UNSET: the AutoResetEvent is currently unset
|
||||
/// - SET: the AutoResetEvent was notified before a wait() was called
|
||||
/// - <std.ResetEvent pointer>: there is an active waiter waiting for a notification.
|
||||
///
|
||||
/// When attempting to wait:
|
||||
/// if the event is unset, it registers a ResetEvent pointer to be notified when the event is set
|
||||
/// if the event is already set, then it consumes the notification and resets the event.
|
||||
///
|
||||
/// When attempting to notify:
|
||||
/// if the event is unset, then we set the event
|
||||
/// if theres a waiting ResetEvent, then we unset the event and notify the ResetEvent
|
||||
///
|
||||
/// This ensures that the event is automatically reset after a wait() has been issued
|
||||
/// and avoids the race condition when using std.ResetEvent in the following scenario:
|
||||
/// thread 1 | thread 2
|
||||
/// std.ResetEvent.wait() |
|
||||
/// | std.ResetEvent.set()
|
||||
/// | std.ResetEvent.set()
|
||||
/// std.ResetEvent.reset() |
|
||||
/// std.ResetEvent.wait() | (missed the second .set() notification above)
|
||||
state: usize = UNSET,
|
||||
|
||||
const UNSET = 0;
|
||||
const SET = 1;
|
||||
|
||||
// the minimum alignment for the `*std.ResetEvent` created by wait*()
|
||||
/// the minimum alignment for the `*std.ResetEvent` created by wait*()
|
||||
const event_align = std.math.max(@alignOf(std.ResetEvent), 2);
|
||||
|
||||
pub fn wait(self: *AutoResetEvent) void {
|
||||
|
||||
43
src/Event.zig
Normal file
43
src/Event.zig
Normal file
@ -0,0 +1,43 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2020 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("std");
|
||||
const Event = @This();
|
||||
|
||||
lock: std.Mutex = .{},
|
||||
event: std.ResetEvent = undefined,
|
||||
state: enum { empty, waiting, notified } = .empty,
|
||||
|
||||
pub fn wait(self: *Event) void {
|
||||
const held = self.lock.acquire();
|
||||
|
||||
switch (self.state) {
|
||||
.empty => {
|
||||
self.state = .waiting;
|
||||
self.event = @TypeOf(self.event).init();
|
||||
held.release();
|
||||
self.event.wait();
|
||||
self.event.deinit();
|
||||
},
|
||||
.waiting => unreachable,
|
||||
.notified => held.release(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(self: *Event) void {
|
||||
const held = self.lock.acquire();
|
||||
|
||||
switch (self.state) {
|
||||
.empty => {
|
||||
self.state = .notified;
|
||||
held.release();
|
||||
},
|
||||
.waiting => {
|
||||
held.release();
|
||||
self.event.set();
|
||||
},
|
||||
.notified => unreachable,
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,8 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2020 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("std");
|
||||
const ThreadPool = @This();
|
||||
|
||||
|
||||
@ -1,9 +1,15 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2020 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("std");
|
||||
const WaitGroup = @This();
|
||||
const Event = @import("Event.zig");
|
||||
|
||||
lock: std.Mutex = .{},
|
||||
counter: usize = 0,
|
||||
event: std.AutoResetEvent = .{},
|
||||
event: Event = .{},
|
||||
|
||||
pub fn start(self: *WaitGroup) void {
|
||||
const held = self.lock.acquire();
|
||||
@ -22,13 +28,15 @@ pub fn stop(self: *WaitGroup) void {
|
||||
}
|
||||
|
||||
pub fn wait(self: *WaitGroup) void {
|
||||
{
|
||||
const held = self.lock.acquire();
|
||||
defer held.release();
|
||||
while (true) {
|
||||
{
|
||||
const held = self.lock.acquire();
|
||||
defer held.release();
|
||||
|
||||
if (self.counter == 0)
|
||||
return;
|
||||
if (self.counter == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
self.event.wait();
|
||||
}
|
||||
|
||||
self.event.wait();
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user