| ... | @@ -21,7 +21,7 @@ pub const ResetEvent = struct { | ... | @@ -21,7 +21,7 @@ pub const ResetEvent = struct { |
| 21 | | 21 | |
| 22 | pub const OsEvent = if (builtin.single_threaded) | 22 | pub const OsEvent = if (builtin.single_threaded) |
| 23 | DebugEvent | 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 | PosixEvent | 25 | PosixEvent |
| 26 | else | 26 | else |
| 27 | AtomicEvent; | 27 | AtomicEvent; |
| ... | @@ -34,11 +34,6 @@ pub const ResetEvent = struct { | ... | @@ -34,11 +34,6 @@ pub const ResetEvent = struct { |
| 34 | self.os_event.deinit(); | 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 | /// Sets the event if not already set and | 37 | /// Sets the event if not already set and |
| 43 | /// wakes up all the threads waiting on the event. | 38 | /// wakes up all the threads waiting on the event. |
| 44 | pub fn set(self: *ResetEvent) void { | 39 | pub fn set(self: *ResetEvent) void { |
| ... | @@ -46,20 +41,28 @@ pub const ResetEvent = struct { | ... | @@ -46,20 +41,28 @@ pub const ResetEvent = struct { |
| 46 | } | 41 | } |
| 47 | | 42 | |
| 48 | /// Resets the event to its original, unset state. | 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 | pub fn reset(self: *ResetEvent) void { | 46 | pub fn reset(self: *ResetEvent) void { |
| 50 | return self.os_event.reset(); | 47 | return self.os_event.reset(); |
| 51 | } | 48 | } |
| 52 | | 49 | |
| 53 | /// Wait for the event to be set by blocking the current thread. | 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 | pub fn wait(self: *ResetEvent) void { | 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 | /// Wait for the event to be set by blocking the current thread. | 58 | /// Wait for the event to be set by blocking the current thread. |
| 59 | /// A timeout in nanoseconds can be provided as a hint for how | 59 | /// A timeout in nanoseconds can be provided as a hint for how |
| 60 | /// long the thread should block on the unset event before throwing error.TimedOut. | 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 | pub fn timedWait(self: *ResetEvent, timeout_ns: u64) !void { | 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,10 +77,6 @@ const DebugEvent = struct { |
| 74 | self.* = undefined; | 77 | self.* = undefined; |
| 75 | } | 78 | } |
| 76 | | 79 | |
| 77 | fn isSet(self: *DebugEvent) bool { | | |
| 78 | return self.is_set; | | |
| 79 | } | | |
| 80 | | | |
| 81 | fn reset(self: *DebugEvent) void { | 80 | fn reset(self: *DebugEvent) void { |
| 82 | self.is_set = false; | 81 | self.is_set = false; |
| 83 | } | 82 | } |
| ... | @@ -86,101 +85,75 @@ const DebugEvent = struct { | ... | @@ -86,101 +85,75 @@ const DebugEvent = struct { |
| 86 | self.is_set = true; | 85 | self.is_set = true; |
| 87 | } | 86 | } |
| 88 | | 87 | |
| 89 | fn wait(self: *DebugEvent, timeout: ?u64) !void { | 88 | fn wait(self: *DebugEvent) void { |
| 90 | if (self.is_set) | 89 | if (self.is_set) |
| 91 | return; | 90 | return; |
| 92 | if (timeout != null) | 91 | |
| 93 | return error.TimedOut; | | |
| 94 | @panic("deadlock detected"); | 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 | const PosixEvent = struct { | 103 | const PosixEvent = struct { |
| 99 | is_set: bool, | 104 | sem: c.sem_t, |
| 100 | cond: c.pthread_cond_t, | | |
| 101 | mutex: c.pthread_mutex_t, | | |
| 102 | | 105 | |
| 103 | fn init() PosixEvent { | 106 | fn init() PosixEvent { |
| 104 | return PosixEvent{ | 107 | return PosixEvent{ |
| 105 | .is_set = false, | 108 | .sem = c.sem_t.init(0, 0), |
| 106 | .cond = c.PTHREAD_COND_INITIALIZER, | | |
| 107 | .mutex = c.PTHREAD_MUTEX_INITIALIZER, | | |
| 108 | }; | 109 | }; |
| 109 | } | 110 | } |
| 110 | | 111 | |
| 111 | fn deinit(self: *PosixEvent) void { | 112 | fn deinit(self: *PosixEvent) void { |
| 112 | // on dragonfly or openbsd, *destroy() functions can return EINVAL | 113 | assert(c.sem_destroy(&self.sem) == 0); |
| 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; | | |
| 130 | } | 114 | } |
| 131 | | 115 | |
| 132 | fn reset(self: *PosixEvent) void { | 116 | fn reset(self: *PosixEvent) void { |
| 133 | assert(c.pthread_mutex_lock(&self.mutex) == 0); | 117 | self.deinit(); |
| 134 | defer assert(c.pthread_mutex_unlock(&self.mutex) == 0); | 118 | assert(c.sem_init(&self.sem, 0, 0) == 0); |
| 135 | | | |
| 136 | self.is_set = false; | | |
| 137 | } | 119 | } |
| 138 | | 120 | |
| 139 | fn set(self: *PosixEvent) void { | 121 | fn set(self: *PosixEvent) void { |
| 140 | assert(c.pthread_mutex_lock(&self.mutex) == 0); | 122 | assert(c.sem_post(&self.sem) == 0); |
| 141 | defer assert(c.pthread_mutex_unlock(&self.mutex) == 0); | 123 | } |
| 142 | | 124 | |
| 143 | if (!self.is_set) { | 125 | fn wait(self: *PosixEvent) void { |
| 144 | self.is_set = true; | 126 | while (true) { |
| 145 | assert(c.pthread_cond_broadcast(&self.cond) == 0); | 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 { | 136 | fn timedWait(self: *PosixEvent, timeout_ns: 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 | | | |
| 157 | var ts: os.timespec = undefined; | 137 | var ts: os.timespec = undefined; |
| 158 | if (timeout) |timeout_ns| { | 138 | var timeout_abs = timeout_ns; |
| 159 | var timeout_abs = timeout_ns; | 139 | if (comptime std.Target.current.isDarwin()) { |
| 160 | if (comptime std.Target.current.isDarwin()) { | 140 | var tv: os.darwin.timeval = undefined; |
| 161 | var tv: os.darwin.timeval = undefined; | 141 | assert(os.darwin.gettimeofday(&tv, null) == 0); |
| 162 | assert(os.darwin.gettimeofday(&tv, null) == 0); | 142 | timeout_abs += @intCast(u64, tv.tv_sec) * time.ns_per_s; |
| 163 | timeout_abs += @intCast(u64, tv.tv_sec) * time.ns_per_s; | 143 | timeout_abs += @intCast(u64, tv.tv_usec) * time.ns_per_us; |
| 164 | timeout_abs += @intCast(u64, tv.tv_usec) * time.ns_per_us; | 144 | } else { |
| 165 | } else { | 145 | os.clock_gettime(os.CLOCK_REALTIME, &ts) catch return error.TimedOut; |
| 166 | os.clock_gettime(os.CLOCK_REALTIME, &ts) catch unreachable; | 146 | timeout_abs += @intCast(u64, ts.tv_sec) * time.ns_per_s; |
| 167 | timeout_abs += @intCast(u64, ts.tv_sec) * time.ns_per_s; | 147 | timeout_abs += @intCast(u64, ts.tv_nsec); |
| 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)); | | |
| 172 | } | 148 | } |
| 173 | | 149 | ts.tv_sec = @intCast(@TypeOf(ts.tv_sec), @divFloor(timeout_abs, time.ns_per_s)); |
| 174 | while (!self.is_set) { | 150 | ts.tv_nsec = @intCast(@TypeOf(ts.tv_nsec), @mod(timeout_abs, time.ns_per_s)); |
| 175 | const rc = switch (timeout == null) { | 151 | while (true) { |
| 176 | true => c.pthread_cond_wait(&self.cond, &self.mutex), | 152 | switch (c.getErrno(c.sem_timedwait(&self.sem, &ts))) { |
| 177 | else => c.pthread_cond_timedwait(&self.cond, &self.mutex, &ts), | 153 | 0 => return, |
| 178 | }; | 154 | c.EINTR => continue, |
| 179 | switch (rc) { | 155 | c.EINVAL => unreachable, |
| 180 | 0 => {}, | 156 | c.ETIMEDOUT => return error.TimedOut, |
| 181 | os.ETIMEDOUT => return error.TimedOut, | | |
| 182 | os.EINVAL => unreachable, | | |
| 183 | os.EPERM => unreachable, | | |
| 184 | else => unreachable, | 157 | else => unreachable, |
| 185 | } | 158 | } |
| 186 | } | 159 | } |
| ... | @@ -201,10 +174,6 @@ const AtomicEvent = struct { | ... | @@ -201,10 +174,6 @@ const AtomicEvent = struct { |
| 201 | self.* = undefined; | 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 | fn reset(self: *AtomicEvent) void { | 177 | fn reset(self: *AtomicEvent) void { |
| 209 | @atomicStore(u32, &self.waiters, 0, .Monotonic); | 178 | @atomicStore(u32, &self.waiters, 0, .Monotonic); |
| 210 | } | 179 | } |
| ... | @@ -216,7 +185,11 @@ const AtomicEvent = struct { | ... | @@ -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 | var waiters = @atomicLoad(u32, &self.waiters, .Acquire); | 193 | var waiters = @atomicLoad(u32, &self.waiters, .Acquire); |
| 221 | while (waiters != WAKE) { | 194 | while (waiters != WAKE) { |
| 222 | waiters = @cmpxchgWeak(u32, &self.waiters, waiters, waiters + WAIT, .Acquire, .Acquire) orelse return Futex.wait(&self.waiters, timeout); | 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,17 +340,17 @@ test "ResetEvent" { |
| 367 | defer event.deinit(); | 340 | defer event.deinit(); |
| 368 | | 341 | |
| 369 | // test event setting | 342 | // test event setting |
| 370 | testing.expect(event.isSet() == false); | | |
| 371 | event.set(); | 343 | event.set(); |
| 372 | testing.expect(event.isSet() == true); | | |
| 373 | | 344 | |
| 374 | // test event resetting | 345 | // test event resetting |
| 375 | event.reset(); | 346 | event.reset(); |
| 376 | testing.expect(event.isSet() == false); | | |
| 377 | | 347 | |
| 378 | // test event waiting (non-blocking) | 348 | // test event waiting (non-blocking) |
| 379 | event.set(); | 349 | event.set(); |
| 380 | event.wait(); | 350 | event.wait(); |
| | 351 | event.reset(); |
| | 352 | |
| | 353 | event.set(); |
| 381 | try event.timedWait(1); | 354 | try event.timedWait(1); |
| 382 | | 355 | |
| 383 | // test cross-thread signaling | 356 | // test cross-thread signaling |