| ... | @@ -38,30 +38,16 @@ const linux = os.linux; | ... | @@ -38,30 +38,16 @@ const linux = os.linux; |
| 38 | const testing = std.testing; | 38 | const testing = std.testing; |
| 39 | const StaticResetEvent = std.thread.StaticResetEvent; | 39 | const StaticResetEvent = std.thread.StaticResetEvent; |
| 40 | | 40 | |
| 41 | pub const Held = struct { | 41 | /// Try to acquire the mutex without blocking. Returns `null` if the mutex is |
| 42 | impl: *Impl, | 42 | /// unavailable. Otherwise returns `Held`. Call `release` on `Held`. |
| 43 | | 43 | pub fn tryAcquire(m: *Mutex) ?Impl.Held { |
| 44 | pub fn release(held: Held) void { | 44 | return m.impl.tryAcquire(); |
| 45 | held.impl.release(); | | |
| 46 | } | | |
| 47 | }; | | |
| 48 | | | |
| 49 | /// Try to acquire the mutex without blocking. Returns null if | | |
| 50 | /// the mutex is unavailable. Otherwise returns Held. Call | | |
| 51 | /// release on Held. | | |
| 52 | pub fn tryAcquire(m: *Mutex) ?Held { | | |
| 53 | if (m.impl.tryAcquire()) { | | |
| 54 | return Held{ .impl = &m.impl }; | | |
| 55 | } else { | | |
| 56 | return null; | | |
| 57 | } | | |
| 58 | } | 45 | } |
| 59 | | 46 | |
| 60 | /// Acquire the mutex. Deadlocks if the mutex is already | 47 | /// Acquire the mutex. Deadlocks if the mutex is already |
| 61 | /// held by the calling thread. | 48 | /// held by the calling thread. |
| 62 | pub fn acquire(m: *Mutex) Held { | 49 | pub fn acquire(m: *Mutex) Impl.Held { |
| 63 | m.impl.acquire(); | 50 | return m.impl.acquire(); |
| 64 | return .{ .impl = &m.impl }; | | |
| 65 | } | 51 | } |
| 66 | | 52 | |
| 67 | const Impl = if (builtin.single_threaded) | 53 | const Impl = if (builtin.single_threaded) |
| ... | @@ -82,25 +68,42 @@ pub const AtomicMutex = struct { | ... | @@ -82,25 +68,42 @@ pub const AtomicMutex = struct { |
| 82 | waiting, | 68 | waiting, |
| 83 | }; | 69 | }; |
| 84 | | 70 | |
| 85 | pub fn tryAcquire(self: *AtomicMutex) bool { | 71 | pub const Held = struct { |
| 86 | return @cmpxchgStrong( | 72 | mutex: *AtomicMutex, |
| | 73 | |
| | 74 | pub fn release(held: Held) void { |
| | 75 | switch (@atomicRmw(State, &held.mutex.state, .Xchg, .unlocked, .Release)) { |
| | 76 | .unlocked => unreachable, |
| | 77 | .locked => {}, |
| | 78 | .waiting => held.mutex.unlockSlow(), |
| | 79 | } |
| | 80 | } |
| | 81 | }; |
| | 82 | |
| | 83 | pub fn tryAcquire(m: *AtomicMutex) ?Held { |
| | 84 | if (@cmpxchgStrong( |
| 87 | State, | 85 | State, |
| 88 | &self.state, | 86 | &m.state, |
| 89 | .unlocked, | 87 | .unlocked, |
| 90 | .locked, | 88 | .locked, |
| 91 | .Acquire, | 89 | .Acquire, |
| 92 | .Monotonic, | 90 | .Monotonic, |
| 93 | ) == null; | 91 | ) == null) { |
| | 92 | return Held{ .mutex = m }; |
| | 93 | } else { |
| | 94 | return null; |
| | 95 | } |
| 94 | } | 96 | } |
| 95 | | 97 | |
| 96 | pub fn acquire(self: *AtomicMutex) void { | 98 | pub fn acquire(m: *AtomicMutex) Held { |
| 97 | switch (@atomicRmw(State, &self.state, .Xchg, .locked, .Acquire)) { | 99 | switch (@atomicRmw(State, &m.state, .Xchg, .locked, .Acquire)) { |
| 98 | .unlocked => {}, | 100 | .unlocked => {}, |
| 99 | else => |s| self.lockSlow(s), | 101 | else => |s| m.lockSlow(s), |
| 100 | } | 102 | } |
| | 103 | return Held{ .mutex = m }; |
| 101 | } | 104 | } |
| 102 | | 105 | |
| 103 | fn lockSlow(self: *AtomicMutex, current_state: State) void { | 106 | fn lockSlow(m: *AtomicMutex, current_state: State) void { |
| 104 | @setCold(true); | 107 | @setCold(true); |
| 105 | var new_state = current_state; | 108 | var new_state = current_state; |
| 106 | | 109 | |
| ... | @@ -108,7 +111,7 @@ pub const AtomicMutex = struct { | ... | @@ -108,7 +111,7 @@ pub const AtomicMutex = struct { |
| 108 | while (spin < 100) : (spin += 1) { | 111 | while (spin < 100) : (spin += 1) { |
| 109 | const state = @cmpxchgWeak( | 112 | const state = @cmpxchgWeak( |
| 110 | State, | 113 | State, |
| 111 | &self.state, | 114 | &m.state, |
| 112 | .unlocked, | 115 | .unlocked, |
| 113 | new_state, | 116 | new_state, |
| 114 | .Acquire, | 117 | .Acquire, |
| ... | @@ -128,14 +131,14 @@ pub const AtomicMutex = struct { | ... | @@ -128,14 +131,14 @@ pub const AtomicMutex = struct { |
| 128 | | 131 | |
| 129 | new_state = .waiting; | 132 | new_state = .waiting; |
| 130 | while (true) { | 133 | while (true) { |
| 131 | switch (@atomicRmw(State, &self.state, .Xchg, new_state, .Acquire)) { | 134 | switch (@atomicRmw(State, &m.state, .Xchg, new_state, .Acquire)) { |
| 132 | .unlocked => return, | 135 | .unlocked => return, |
| 133 | else => {}, | 136 | else => {}, |
| 134 | } | 137 | } |
| 135 | switch (std.Target.current.os.tag) { | 138 | switch (std.Target.current.os.tag) { |
| 136 | .linux => { | 139 | .linux => { |
| 137 | switch (linux.getErrno(linux.futex_wait( | 140 | switch (linux.getErrno(linux.futex_wait( |
| 138 | @ptrCast(*const i32, &self.state), | 141 | @ptrCast(*const i32, &m.state), |
| 139 | linux.FUTEX_PRIVATE_FLAG | linux.FUTEX_WAIT, | 142 | linux.FUTEX_PRIVATE_FLAG | linux.FUTEX_WAIT, |
| 140 | @enumToInt(new_state), | 143 | @enumToInt(new_state), |
| 141 | null, | 144 | null, |
| ... | @@ -151,21 +154,13 @@ pub const AtomicMutex = struct { | ... | @@ -151,21 +154,13 @@ pub const AtomicMutex = struct { |
| 151 | } | 154 | } |
| 152 | } | 155 | } |
| 153 | | 156 | |
| 154 | pub fn release(self: *AtomicMutex) void { | 157 | fn unlockSlow(m: *AtomicMutex) void { |
| 155 | switch (@atomicRmw(State, &self.state, .Xchg, .unlocked, .Release)) { | | |
| 156 | .unlocked => unreachable, | | |
| 157 | .locked => {}, | | |
| 158 | .waiting => self.unlockSlow(), | | |
| 159 | } | | |
| 160 | } | | |
| 161 | | | |
| 162 | fn unlockSlow(self: *AtomicMutex) void { | | |
| 163 | @setCold(true); | 158 | @setCold(true); |
| 164 | | 159 | |
| 165 | switch (std.Target.current.os.tag) { | 160 | switch (std.Target.current.os.tag) { |
| 166 | .linux => { | 161 | .linux => { |
| 167 | switch (linux.getErrno(linux.futex_wake( | 162 | switch (linux.getErrno(linux.futex_wake( |
| 168 | @ptrCast(*const i32, &self.state), | 163 | @ptrCast(*const i32, &m.state), |
| 169 | linux.FUTEX_PRIVATE_FLAG | linux.FUTEX_WAKE, | 164 | linux.FUTEX_PRIVATE_FLAG | linux.FUTEX_WAKE, |
| 170 | 1, | 165 | 1, |
| 171 | ))) { | 166 | ))) { |
| ... | @@ -182,18 +177,36 @@ pub const AtomicMutex = struct { | ... | @@ -182,18 +177,36 @@ pub const AtomicMutex = struct { |
| 182 | pub const PthreadMutex = struct { | 177 | pub const PthreadMutex = struct { |
| 183 | pthread_mutex: std.c.pthread_mutex_t = .{}, | 178 | pthread_mutex: std.c.pthread_mutex_t = .{}, |
| 184 | | 179 | |
| | 180 | pub const Held = struct { |
| | 181 | mutex: *PthreadMutex, |
| | 182 | |
| | 183 | pub fn release(held: Held) void { |
| | 184 | switch (std.c.pthread_mutex_unlock(&held.mutex.pthread_mutex)) { |
| | 185 | 0 => return, |
| | 186 | std.c.EINVAL => unreachable, |
| | 187 | std.c.EAGAIN => unreachable, |
| | 188 | std.c.EPERM => unreachable, |
| | 189 | else => unreachable, |
| | 190 | } |
| | 191 | } |
| | 192 | }; |
| | 193 | |
| 185 | /// Try to acquire the mutex without blocking. Returns null if | 194 | /// Try to acquire the mutex without blocking. Returns null if |
| 186 | /// the mutex is unavailable. Otherwise returns Held. Call | 195 | /// the mutex is unavailable. Otherwise returns Held. Call |
| 187 | /// release on Held. | 196 | /// release on Held. |
| 188 | pub fn tryAcquire(self: *PthreadMutex) bool { | 197 | pub fn tryAcquire(m: *PthreadMutex) ?Held { |
| 189 | return std.c.pthread_mutex_trylock(&self.pthread_mutex) == 0; | 198 | if (std.c.pthread_mutex_trylock(&m.pthread_mutex) == 0) { |
| | 199 | return Held{ .mutex = m }; |
| | 200 | } else { |
| | 201 | return null; |
| | 202 | } |
| 190 | } | 203 | } |
| 191 | | 204 | |
| 192 | /// Acquire the mutex. Will deadlock if the mutex is already | 205 | /// Acquire the mutex. Will deadlock if the mutex is already |
| 193 | /// held by the calling thread. | 206 | /// held by the calling thread. |
| 194 | pub fn acquire(self: *PthreadMutex) void { | 207 | pub fn acquire(m: *PthreadMutex) Held { |
| 195 | switch (std.c.pthread_mutex_lock(&self.pthread_mutex)) { | 208 | switch (std.c.pthread_mutex_lock(&m.pthread_mutex)) { |
| 196 | 0 => return, | 209 | 0 => return Held{ .mutex = m }, |
| 197 | std.c.EINVAL => unreachable, | 210 | std.c.EINVAL => unreachable, |
| 198 | std.c.EBUSY => unreachable, | 211 | std.c.EBUSY => unreachable, |
| 199 | std.c.EAGAIN => unreachable, | 212 | std.c.EAGAIN => unreachable, |
| ... | @@ -202,16 +215,6 @@ pub const PthreadMutex = struct { | ... | @@ -202,16 +215,6 @@ pub const PthreadMutex = struct { |
| 202 | else => unreachable, | 215 | else => unreachable, |
| 203 | } | 216 | } |
| 204 | } | 217 | } |
| 205 | | | |
| 206 | pub fn release(self: *PthreadMutex) void { | | |
| 207 | switch (std.c.pthread_mutex_unlock(&self.pthread_mutex)) { | | |
| 208 | 0 => return, | | |
| 209 | std.c.EINVAL => unreachable, | | |
| 210 | std.c.EAGAIN => unreachable, | | |
| 211 | std.c.EPERM => unreachable, | | |
| 212 | else => unreachable, | | |
| 213 | } | | |
| 214 | } | | |
| 215 | }; | 218 | }; |
| 216 | | 219 | |
| 217 | /// This has the sematics as `Mutex`, however it does not actually do any | 220 | /// This has the sematics as `Mutex`, however it does not actually do any |
| ... | @@ -221,43 +224,56 @@ pub const Dummy = struct { | ... | @@ -221,43 +224,56 @@ pub const Dummy = struct { |
| 221 | | 224 | |
| 222 | const lock_init = if (std.debug.runtime_safety) false else {}; | 225 | const lock_init = if (std.debug.runtime_safety) false else {}; |
| 223 | | 226 | |
| | 227 | pub const Held = struct { |
| | 228 | mutex: *Dummy, |
| | 229 | |
| | 230 | pub fn release(held: Held) void { |
| | 231 | if (std.debug.runtime_safety) { |
| | 232 | held.mutex.lock = false; |
| | 233 | } |
| | 234 | } |
| | 235 | }; |
| | 236 | |
| 224 | /// Try to acquire the mutex without blocking. Returns null if | 237 | /// Try to acquire the mutex without blocking. Returns null if |
| 225 | /// the mutex is unavailable. Otherwise returns Held. Call | 238 | /// the mutex is unavailable. Otherwise returns Held. Call |
| 226 | /// release on Held. | 239 | /// release on Held. |
| 227 | pub fn tryAcquire(self: *Dummy) bool { | 240 | pub fn tryAcquire(m: *Dummy) ?Held { |
| 228 | if (std.debug.runtime_safety) { | 241 | if (std.debug.runtime_safety) { |
| 229 | if (self.lock) return false; | 242 | if (m.lock) return null; |
| 230 | self.lock = true; | 243 | m.lock = true; |
| 231 | } | 244 | } |
| 232 | return true; | 245 | return Held{ .mutex = m }; |
| 233 | } | 246 | } |
| 234 | | 247 | |
| 235 | /// Acquire the mutex. Will deadlock if the mutex is already | 248 | /// Acquire the mutex. Will deadlock if the mutex is already |
| 236 | /// held by the calling thread. | 249 | /// held by the calling thread. |
| 237 | pub fn acquire(self: *Dummy) void { | 250 | pub fn acquire(m: *Dummy) Held { |
| 238 | return self.tryAcquire() orelse @panic("deadlock detected"); | 251 | return m.tryAcquire() orelse @panic("deadlock detected"); |
| 239 | } | | |
| 240 | | | |
| 241 | pub fn release(self: *Dummy) void { | | |
| 242 | if (std.debug.runtime_safety) { | | |
| 243 | self.mutex.lock = false; | | |
| 244 | } | | |
| 245 | } | 252 | } |
| 246 | }; | 253 | }; |
| 247 | | 254 | |
| 248 | const WindowsMutex = struct { | 255 | const WindowsMutex = struct { |
| 249 | srwlock: windows.SRWLOCK = windows.SRWLOCK_INIT, | 256 | srwlock: windows.SRWLOCK = windows.SRWLOCK_INIT, |
| 250 | | 257 | |
| 251 | pub fn tryAcquire(self: *WindowsMutex) bool { | 258 | pub const Held = struct { |
| 252 | return TryAcquireSRWLockExclusive(&self.srwlock) != system.FALSE; | 259 | mutex: *WindowsMutex, |
| 253 | } | | |
| 254 | | 260 | |
| 255 | pub fn acquire(self: *WindowsMutex) void { | 261 | pub fn release(held: Held) void { |
| 256 | AcquireSRWLockExclusive(&self.srwlock); | 262 | windows.ReleaseSRWLockExclusive(&held.mutex.srwlock); |
| | 263 | } |
| | 264 | }; |
| | 265 | |
| | 266 | pub fn tryAcquire(m: *WindowsMutex) ?Held { |
| | 267 | if (windows.TryAcquireSRWLockExclusive(&m.srwlock) != windows.FALSE) { |
| | 268 | return Held{ .mutex = m }; |
| | 269 | } else { |
| | 270 | return null; |
| | 271 | } |
| 257 | } | 272 | } |
| 258 | | 273 | |
| 259 | pub fn release(self: *WindowsMutex) void { | 274 | pub fn acquire(m: *WindowsMutex) Held { |
| 260 | ReleaseSRWLockExclusive(&self.srwlock); | 275 | windows.AcquireSRWLockExclusive(&m.srwlock); |
| | 276 | return Held{ .mutex = m }; |
| 261 | } | 277 | } |
| 262 | }; | 278 | }; |
| 263 | | 279 | |