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 {...@@ -43,99 +43,132 @@ pub const AutoResetEvent = struct {
43 const event_align = std.math.max(@alignOf(std.ResetEvent), 2);43 const event_align = std.math.max(@alignOf(std.ResetEvent), 2);
4444
45 pub fn wait(self: *AutoResetEvent) void {45 pub fn wait(self: *AutoResetEvent) void {
46 self.waitInner(null) catch unreachable;46 self.waitFor(null) catch unreachable;
47 }47 }
4848
49 pub fn timedWait(self: *AutoResetEvent, timeout: u64) error{TimedOut}!void {49 pub fn timedWait(self: *AutoResetEvent, timeout: u64) error{TimedOut}!void {
50 return self.waitInner(timeout);50 return self.waitFor(timeout);
51 }51 }
5252
53 fn waitInner(self: *AutoResetEvent, timeout: ?u64) error{TimedOut}!void {53 fn waitFor(self: *AutoResetEvent, timeout: ?u64) error{TimedOut}!void {
54 // the local ResetEvent is lazily initialized54 // lazily initialized std.ResetEvent
55 var has_reset_event = false;
56 var reset_event: std.ResetEvent align(event_align) = undefined;55 var reset_event: std.ResetEvent align(event_align) = undefined;
56 var has_reset_event = false;
57 defer if (has_reset_event) {57 defer if (has_reset_event) {
58 reset_event.deinit();58 reset_event.deinit();
59 };59 };
6060
61 var state = @atomicLoad(usize, &self.state, .SeqCst);61 var state = @atomicLoad(usize, &self.state, .SeqCst);
62 while (true) {62 while (true) {
63 switch (state) {63 // consume a notification if there is any
64 UNSET => {64 if (state == SET) {
65 if (!has_reset_event) {65 @atomicStore(usize, &self.state, UNSET, .SeqCst);
66 has_reset_event = true;66 return;
67 reset_event = std.ResetEvent.init();67 }
68 }68
69 state = @cmpxchgWeak(69 // check if theres currently a pending ResetEvent pointer already registered
70 usize,70 if (state != UNSET) {
71 &self.state,71 unreachable; // multiple waiting threads on the same AutoResetEvent
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 }
103 }72 }
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;
104 }129 }
105 }130 }
106131
107 pub fn set(self: *AutoResetEvent) void {132 pub fn set(self: *AutoResetEvent) void {
108 var state = @atomicLoad(usize, &self.state, .SeqCst);133 var state = @atomicLoad(usize, &self.state, .SeqCst);
109 while (true) {134 while (true) {
110 switch (state) {135 // If the AutoResetEvent is already set, there is nothing else left to do
111 UNSET => {136 if (state == SET) {
112 state = @cmpxchgWeak(137 return;
113 usize,138 }
114 &self.state,139
115 state,140 // If the AutoResetEvent isn't set,
116 SET,141 // then try to leave a notification for the wait() thread that we set() it.
117 .SeqCst,142 if (state == UNSET) {
118 .SeqCst,143 state = @cmpxchgWeak(
119 ) orelse return;144 usize,
120 },145 &self.state,
121 SET => {146 UNSET,
122 return;147 SET,
123 },148 .SeqCst,
124 else => |reset_event_ptr| {149 .SeqCst,
125 state = @cmpxchgWeak(150 ) orelse return;
126 usize,151 continue;
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 }
138 }152 }
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;
139 }172 }
140 }173 }
141};174};
...@@ -161,7 +194,6 @@ test "std.AutoResetEvent" {...@@ -161,7 +194,6 @@ test "std.AutoResetEvent" {
161 const Self = @This();194 const Self = @This();
162195
163 fn sender(self: *Self) void {196 fn sender(self: *Self) void {
164 std.debug.print("\n", .{});
165 testing.expect(self.value == 0);197 testing.expect(self.value == 0);
166 self.value = 1;198 self.value = 1;
167 self.out.set();199 self.out.set();
lib/std/event/loop.zig+3
...@@ -862,8 +862,11 @@ pub const Loop = struct {...@@ -862,8 +862,11 @@ pub const Loop = struct {
862 const held = self.entries.mutex.acquire();862 const held = self.entries.mutex.acquire();
863 defer held.release();863 defer held.release();
864864
865 // starting from the head
865 var head = self.entries.head orelse return null;866 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
867 var min = head;870 var min = head;
868 while (head.next) |node| {871 while (head.next) |node| {
869 const minEntry = @fieldParentPtr(Entry, "node", min);872 const minEntry = @fieldParentPtr(Entry, "node", min);