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