| ... | ... | @@ -21,7 +21,7 @@ pub const ResetEvent = struct { |
| 21 | 21 | |
| 22 | 22 | pub const OsEvent = if (builtin.single_threaded) |
| 23 | 23 | DebugEvent |
| 24 | | else if (builtin.link_libc and builtin.os.tag != .windows and builtin.os.tag != .linux) |
| 24 | else if (std.Thread.use_pthreads) |
| 25 | 25 | PosixEvent |
| 26 | 26 | else |
| 27 | 27 | AtomicEvent; |
| ... | ... | @@ -34,11 +34,6 @@ pub const ResetEvent = struct { |
| 34 | 34 | self.os_event.deinit(); |
| 35 | 35 | } |
| 36 | 36 | |
| 37 | | /// Returns whether or not the event is currenetly set |
| 38 | | pub fn isSet(self: *ResetEvent) bool { |
| 39 | | return self.os_event.isSet(); |
| 40 | | } |
| 41 | | |
| 42 | 37 | /// Sets the event if not already set and |
| 43 | 38 | /// wakes up all the threads waiting on the event. |
| 44 | 39 | pub fn set(self: *ResetEvent) void { |
| ... | ... | @@ -46,20 +41,28 @@ pub const ResetEvent = struct { |
| 46 | 41 | } |
| 47 | 42 | |
| 48 | 43 | /// Resets the event to its original, unset state. |
| 44 | /// TODO improve these docs: |
| 45 | /// * under what circumstances does it make sense to call this function? |
| 49 | 46 | pub fn reset(self: *ResetEvent) void { |
| 50 | 47 | return self.os_event.reset(); |
| 51 | 48 | } |
| 52 | 49 | |
| 53 | 50 | /// Wait for the event to be set by blocking the current thread. |
| 51 | /// TODO improve these docs: |
| 52 | /// * is the function thread-safe? |
| 53 | /// * does it have suprious wakeups? |
| 54 | 54 | pub fn wait(self: *ResetEvent) void { |
| 55 | | return self.os_event.wait(null) catch unreachable; |
| 55 | return self.os_event.wait(); |
| 56 | 56 | } |
| 57 | 57 | |
| 58 | 58 | /// Wait for the event to be set by blocking the current thread. |
| 59 | 59 | /// A timeout in nanoseconds can be provided as a hint for how |
| 60 | 60 | /// long the thread should block on the unset event before throwing error.TimedOut. |
| 61 | /// TODO improve these docs: |
| 62 | /// * is the function thread-safe? |
| 63 | /// * does it have suprious wakeups? |
| 61 | 64 | pub fn timedWait(self: *ResetEvent, timeout_ns: u64) !void { |
| 62 | | return self.os_event.wait(timeout_ns); |
| 65 | return self.os_event.timedWait(timeout_ns); |
| 63 | 66 | } |
| 64 | 67 | }; |
| 65 | 68 | |
| ... | ... | @@ -74,10 +77,6 @@ const DebugEvent = struct { |
| 74 | 77 | self.* = undefined; |
| 75 | 78 | } |
| 76 | 79 | |
| 77 | | fn isSet(self: *DebugEvent) bool { |
| 78 | | return self.is_set; |
| 79 | | } |
| 80 | | |
| 81 | 80 | fn reset(self: *DebugEvent) void { |
| 82 | 81 | self.is_set = false; |
| 83 | 82 | } |
| ... | ... | @@ -86,101 +85,75 @@ const DebugEvent = struct { |
| 86 | 85 | self.is_set = true; |
| 87 | 86 | } |
| 88 | 87 | |
| 89 | | fn wait(self: *DebugEvent, timeout: ?u64) !void { |
| 88 | fn wait(self: *DebugEvent) void { |
| 90 | 89 | if (self.is_set) |
| 91 | 90 | return; |
| 92 | | if (timeout != null) |
| 93 | | return error.TimedOut; |
| 91 | |
| 94 | 92 | @panic("deadlock detected"); |
| 95 | 93 | } |
| 94 | |
| 95 | fn timedWait(self: *DebugEvent, timeout: u64) !void { |
| 96 | if (self.is_set) |
| 97 | return; |
| 98 | |
| 99 | return error.TimedOut; |
| 100 | } |
| 96 | 101 | }; |
| 97 | 102 | |
| 98 | 103 | const PosixEvent = struct { |
| 99 | | is_set: bool, |
| 100 | | cond: c.pthread_cond_t, |
| 101 | | mutex: c.pthread_mutex_t, |
| 104 | sem: c.sem_t, |
| 102 | 105 | |
| 103 | 106 | fn init() PosixEvent { |
| 104 | 107 | return PosixEvent{ |
| 105 | | .is_set = false, |
| 106 | | .cond = c.PTHREAD_COND_INITIALIZER, |
| 107 | | .mutex = c.PTHREAD_MUTEX_INITIALIZER, |
| 108 | .sem = c.sem_t.init(0, 0), |
| 108 | 109 | }; |
| 109 | 110 | } |
| 110 | 111 | |
| 111 | 112 | fn deinit(self: *PosixEvent) void { |
| 112 | | // on dragonfly or openbsd, *destroy() functions can return EINVAL |
| 113 | | // for statically initialized pthread structures |
| 114 | | const err = if (builtin.os.tag == .dragonfly or builtin.os.tag == .openbsd) |
| 115 | | os.EINVAL |
| 116 | | else |
| 117 | | 0; |
| 118 | | |
| 119 | | const retm = c.pthread_mutex_destroy(&self.mutex); |
| 120 | | assert(retm == 0 or retm == err); |
| 121 | | const retc = c.pthread_cond_destroy(&self.cond); |
| 122 | | assert(retc == 0 or retc == err); |
| 123 | | } |
| 124 | | |
| 125 | | fn isSet(self: *PosixEvent) bool { |
| 126 | | assert(c.pthread_mutex_lock(&self.mutex) == 0); |
| 127 | | defer assert(c.pthread_mutex_unlock(&self.mutex) == 0); |
| 128 | | |
| 129 | | return self.is_set; |
| 113 | assert(c.sem_destroy(&self.sem) == 0); |
| 130 | 114 | } |
| 131 | 115 | |
| 132 | 116 | fn reset(self: *PosixEvent) void { |
| 133 | | assert(c.pthread_mutex_lock(&self.mutex) == 0); |
| 134 | | defer assert(c.pthread_mutex_unlock(&self.mutex) == 0); |
| 135 | | |
| 136 | | self.is_set = false; |
| 117 | self.deinit(); |
| 118 | assert(c.sem_init(&self.sem, 0, 0) == 0); |
| 137 | 119 | } |
| 138 | 120 | |
| 139 | 121 | fn set(self: *PosixEvent) void { |
| 140 | | assert(c.pthread_mutex_lock(&self.mutex) == 0); |
| 141 | | defer assert(c.pthread_mutex_unlock(&self.mutex) == 0); |
| 122 | assert(c.sem_post(&self.sem) == 0); |
| 123 | } |
| 142 | 124 | |
| 143 | | if (!self.is_set) { |
| 144 | | self.is_set = true; |
| 145 | | assert(c.pthread_cond_broadcast(&self.cond) == 0); |
| 125 | fn wait(self: *PosixEvent) void { |
| 126 | while (true) { |
| 127 | switch (c.getErrno(c.sem_wait(&self.sem))) { |
| 128 | 0 => return, |
| 129 | c.EINTR => continue, |
| 130 | c.EINVAL => unreachable, |
| 131 | else => unreachable, |
| 132 | } |
| 146 | 133 | } |
| 147 | 134 | } |
| 148 | 135 | |
| 149 | | fn wait(self: *PosixEvent, timeout: ?u64) !void { |
| 150 | | assert(c.pthread_mutex_lock(&self.mutex) == 0); |
| 151 | | defer assert(c.pthread_mutex_unlock(&self.mutex) == 0); |
| 152 | | |
| 153 | | // quick guard before possibly calling time syscalls below |
| 154 | | if (self.is_set) |
| 155 | | return; |
| 156 | | |
| 136 | fn timedWait(self: *PosixEvent, timeout_ns: u64) !void { |
| 157 | 137 | var ts: os.timespec = undefined; |
| 158 | | if (timeout) |timeout_ns| { |
| 159 | | var timeout_abs = timeout_ns; |
| 160 | | if (comptime std.Target.current.isDarwin()) { |
| 161 | | var tv: os.darwin.timeval = undefined; |
| 162 | | assert(os.darwin.gettimeofday(&tv, null) == 0); |
| 163 | | timeout_abs += @intCast(u64, tv.tv_sec) * time.ns_per_s; |
| 164 | | timeout_abs += @intCast(u64, tv.tv_usec) * time.ns_per_us; |
| 165 | | } else { |
| 166 | | os.clock_gettime(os.CLOCK_REALTIME, &ts) catch unreachable; |
| 167 | | timeout_abs += @intCast(u64, ts.tv_sec) * time.ns_per_s; |
| 168 | | timeout_abs += @intCast(u64, ts.tv_nsec); |
| 169 | | } |
| 170 | | ts.tv_sec = @intCast(@TypeOf(ts.tv_sec), @divFloor(timeout_abs, time.ns_per_s)); |
| 171 | | ts.tv_nsec = @intCast(@TypeOf(ts.tv_nsec), @mod(timeout_abs, time.ns_per_s)); |
| 138 | var timeout_abs = timeout_ns; |
| 139 | if (comptime std.Target.current.isDarwin()) { |
| 140 | var tv: os.darwin.timeval = undefined; |
| 141 | assert(os.darwin.gettimeofday(&tv, null) == 0); |
| 142 | timeout_abs += @intCast(u64, tv.tv_sec) * time.ns_per_s; |
| 143 | timeout_abs += @intCast(u64, tv.tv_usec) * time.ns_per_us; |
| 144 | } else { |
| 145 | os.clock_gettime(os.CLOCK_REALTIME, &ts) catch return error.TimedOut; |
| 146 | timeout_abs += @intCast(u64, ts.tv_sec) * time.ns_per_s; |
| 147 | timeout_abs += @intCast(u64, ts.tv_nsec); |
| 172 | 148 | } |
| 173 | | |
| 174 | | while (!self.is_set) { |
| 175 | | const rc = switch (timeout == null) { |
| 176 | | true => c.pthread_cond_wait(&self.cond, &self.mutex), |
| 177 | | else => c.pthread_cond_timedwait(&self.cond, &self.mutex, &ts), |
| 178 | | }; |
| 179 | | switch (rc) { |
| 180 | | 0 => {}, |
| 181 | | os.ETIMEDOUT => return error.TimedOut, |
| 182 | | os.EINVAL => unreachable, |
| 183 | | os.EPERM => unreachable, |
| 149 | ts.tv_sec = @intCast(@TypeOf(ts.tv_sec), @divFloor(timeout_abs, time.ns_per_s)); |
| 150 | ts.tv_nsec = @intCast(@TypeOf(ts.tv_nsec), @mod(timeout_abs, time.ns_per_s)); |
| 151 | while (true) { |
| 152 | switch (c.getErrno(c.sem_timedwait(&self.sem, &ts))) { |
| 153 | 0 => return, |
| 154 | c.EINTR => continue, |
| 155 | c.EINVAL => unreachable, |
| 156 | c.ETIMEDOUT => return error.TimedOut, |
| 184 | 157 | else => unreachable, |
| 185 | 158 | } |
| 186 | 159 | } |
| ... | ... | @@ -201,10 +174,6 @@ const AtomicEvent = struct { |
| 201 | 174 | self.* = undefined; |
| 202 | 175 | } |
| 203 | 176 | |
| 204 | | fn isSet(self: *const AtomicEvent) bool { |
| 205 | | return @atomicLoad(u32, &self.waiters, .Acquire) == WAKE; |
| 206 | | } |
| 207 | | |
| 208 | 177 | fn reset(self: *AtomicEvent) void { |
| 209 | 178 | @atomicStore(u32, &self.waiters, 0, .Monotonic); |
| 210 | 179 | } |
| ... | ... | @@ -216,7 +185,11 @@ const AtomicEvent = struct { |
| 216 | 185 | } |
| 217 | 186 | } |
| 218 | 187 | |
| 219 | | fn wait(self: *AtomicEvent, timeout: ?u64) !void { |
| 188 | fn wait(self: *AtomicEvent) void { |
| 189 | return self.timedWait(null) catch unreachable; |
| 190 | } |
| 191 | |
| 192 | fn timedWait(self: *AtomicEvent, timeout: ?u64) !void { |
| 220 | 193 | var waiters = @atomicLoad(u32, &self.waiters, .Acquire); |
| 221 | 194 | while (waiters != WAKE) { |
| 222 | 195 | waiters = @cmpxchgWeak(u32, &self.waiters, waiters, waiters + WAIT, .Acquire, .Acquire) orelse return Futex.wait(&self.waiters, timeout); |
| ... | ... | @@ -367,17 +340,17 @@ test "ResetEvent" { |
| 367 | 340 | defer event.deinit(); |
| 368 | 341 | |
| 369 | 342 | // test event setting |
| 370 | | testing.expect(event.isSet() == false); |
| 371 | 343 | event.set(); |
| 372 | | testing.expect(event.isSet() == true); |
| 373 | 344 | |
| 374 | 345 | // test event resetting |
| 375 | 346 | event.reset(); |
| 376 | | testing.expect(event.isSet() == false); |
| 377 | 347 | |
| 378 | 348 | // test event waiting (non-blocking) |
| 379 | 349 | event.set(); |
| 380 | 350 | event.wait(); |
| 351 | event.reset(); |
| 352 | |
| 353 | event.set(); |
| 381 | 354 | try event.timedWait(1); |
| 382 | 355 | |
| 383 | 356 | // test cross-thread signaling |