authorgravatar for 45520026+kprotty@users.noreply.github.comkprotty <45520026+kprotty@users.noreply.github.com> 2020-10-11 19:16:07-05:00
committergravatar for 45520026+kprotty@users.noreply.github.comkprotty <45520026+kprotty@users.noreply.github.com> 2020-10-11 19:16:07-05:00
log12508025a4b3a841819b913e4ed88810ca04ba79
tree3148b9bda10afef652bea741d3170f6b7072cdd6
parente9a4c3dd82eb88d2b0759cded48794642f192bbd

Add more comments & cleanup AutoResetEvent


2 files changed, 109 insertions(+), 74 deletions(-)

lib/std/auto_reset_event.zig+106-74
......@@ -43,99 +43,132 @@ pub const AutoResetEvent = struct {
4343 const event_align = std.math.max(@alignOf(std.ResetEvent), 2);
4444
4545 pub fn wait(self: *AutoResetEvent) void {
46 self.waitInner(null) catch unreachable;
46 self.waitFor(null) catch unreachable;
4747 }
4848
4949 pub fn timedWait(self: *AutoResetEvent, timeout: u64) error{TimedOut}!void {
50 return self.waitInner(timeout);
50 return self.waitFor(timeout);
5151 }
5252
53 fn waitInner(self: *AutoResetEvent, timeout: ?u64) error{TimedOut}!void {
54 // the local ResetEvent is lazily initialized
55 var has_reset_event = false;
53 fn waitFor(self: *AutoResetEvent, timeout: ?u64) error{TimedOut}!void {
54 // lazily initialized std.ResetEvent
5655 var reset_event: std.ResetEvent align(event_align) = undefined;
56 var has_reset_event = false;
5757 defer if (has_reset_event) {
5858 reset_event.deinit();
5959 };
6060
6161 var state = @atomicLoad(usize, &self.state, .SeqCst);
6262 while (true) {
63 switch (state) {
64 UNSET => {
65 if (!has_reset_event) {
66 has_reset_event = true;
67 reset_event = std.ResetEvent.init();
68 }
69 state = @cmpxchgWeak(
70 usize,
71 &self.state,
72 state,
73 @ptrToInt(&reset_event),
74 .SeqCst,
75 .SeqCst,
76 ) orelse {
77 if (timeout) |timeout_ns| {
78 reset_event.timedWait(timeout_ns) catch {
79 state = @cmpxchgStrong(
80 usize,
81 &self.state,
82 @ptrToInt(&reset_event),
83 UNSET,
84 .SeqCst,
85 .SeqCst,
86 ) orelse return error.TimedOut;
87 assert(state == SET);
88 reset_event.wait();
89 };
90 } else {
91 reset_event.wait();
92 }
93 return;
94 };
95 },
96 SET => {
97 @atomicStore(usize, &self.state, UNSET, .SeqCst);
98 return;
99 },
100 else => {
101 unreachable; // multiple waiters on the same Event
102 }
63 // consume a notification if there is any
64 if (state == SET) {
65 @atomicStore(usize, &self.state, UNSET, .SeqCst);
66 return;
67 }
68
69 // check if theres currently a pending ResetEvent pointer already registered
70 if (state != UNSET) {
71 unreachable; // multiple waiting threads on the same AutoResetEvent
10372 }
73
74 // lazily initialize the ResetEvent if it hasn't been already
75 if (!has_reset_event) {
76 has_reset_event = true;
77 reset_event = std.ResetEvent.init();
78 }
79
80 // Since the AutoResetEvent currently isnt set,
81 // try to register our ResetEvent on it to wait
82 // for a set() call from another thread.
83 if (@cmpxchgWeak(
84 usize,
85 &self.state,
86 UNSET,
87 @ptrToInt(&reset_event),
88 .SeqCst,
89 .SeqCst,
90 )) |new_state| {
91 state = new_state;
92 continue;
93 }
94
95 // if no timeout was specified, then just wait forever
96 const timeout_ns = timeout orelse {
97 reset_event.wait();
98 return;
99 };
100
101 // wait with a timeout and return if signalled via set()
102 if (reset_event.timedWait(timeout_ns)) |_| {
103 return;
104 } else |timed_out| {}
105
106 // If we timed out, we need to transition the AutoResetEvent back to UNSET.
107 // If we don't, then when we return, a set() thread could observe a pointer to an invalid ResetEvent.
108 state = @cmpxchgStrong(
109 usize,
110 &self.state,
111 @ptrToInt(&reset_event),
112 UNSET,
113 .SeqCst,
114 .SeqCst,
115 ) orelse return error.TimedOut;
116
117 // We didn't manage to unregister ourselves from the state.
118 if (state == SET) {
119 unreachable; // AutoResetEvent notified without waking up the waiting thread
120 } else if (state != UNSET) {
121 unreachable; // multiple waiting threads on the same AutoResetEvent observed when timing out
122 }
123
124 // This menas a set() thread saw our ResetEvent pointer, acquired it, and is trying to wake it up.
125 // We need to wait for it to wake up our ResetEvent before we can return and invalidate it.
126 // We don't return error.TimedOut here as it technically notified us while we were "timing out".
127 reset_event.wait();
128 return;
104129 }
105130 }
106131
107132 pub fn set(self: *AutoResetEvent) void {
108133 var state = @atomicLoad(usize, &self.state, .SeqCst);
109134 while (true) {
110 switch (state) {
111 UNSET => {
112 state = @cmpxchgWeak(
113 usize,
114 &self.state,
115 state,
116 SET,
117 .SeqCst,
118 .SeqCst,
119 ) orelse return;
120 },
121 SET => {
122 return;
123 },
124 else => |reset_event_ptr| {
125 state = @cmpxchgWeak(
126 usize,
127 &self.state,
128 state,
129 UNSET,
130 .SeqCst,
131 .SeqCst,
132 ) orelse {
133 const reset_event = @intToPtr(*align(event_align) std.ResetEvent, reset_event_ptr);
134 reset_event.set();
135 return;
136 };
137 }
135 // If the AutoResetEvent is already set, there is nothing else left to do
136 if (state == SET) {
137 return;
138 }
139
140 // If the AutoResetEvent isn't set,
141 // then try to leave a notification for the wait() thread that we set() it.
142 if (state == UNSET) {
143 state = @cmpxchgWeak(
144 usize,
145 &self.state,
146 UNSET,
147 SET,
148 .SeqCst,
149 .SeqCst,
150 ) orelse return;
151 continue;
138152 }
153
154 // There is a ResetEvent pointer registered on the AutoResetEvent event thats waiting.
155 // Try to acquire ownership of it so that we can wake it up.
156 // This also resets the AutoResetEvent so that there is no race condition as defined above.
157 if (@cmpxchgWeak(
158 usize,
159 &self.state,
160 state,
161 UNSET,
162 .SeqCst,
163 .SeqCst,
164 )) |new_state| {
165 state = new_state;
166 continue;
167 }
168
169 const reset_event = @intToPtr(*align(event_align) std.ResetEvent, state);
170 reset_event.set();
171 return;
139172 }
140173 }
141174};
......@@ -161,7 +194,6 @@ test "std.AutoResetEvent" {
161194 const Self = @This();
162195
163196 fn sender(self: *Self) void {
164 std.debug.print("\n", .{});
165197 testing.expect(self.value == 0);
166198 self.value = 1;
167199 self.out.set();
lib/std/event/loop.zig+3
......@@ -862,8 +862,11 @@ pub const Loop = struct {
862862 const held = self.entries.mutex.acquire();
863863 defer held.release();
864864
865 // starting from the head
865866 var head = self.entries.head orelse return null;
866867
868 // traverse the list of waiting entires to
869 // find the Node with the smallest `expires` field
867870 var min = head;
868871 while (head.next) |node| {
869872 const minEntry = @fieldParentPtr(Entry, "node", min);