authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-14 21:28:22-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-14 21:28:22-07:00
log9698ea3173351a66de8950c5124f22e80f6c9fc8
treeaa72e4e50a83be82d9056ba4d7efa3263c89706b
parenta9667b5a859a589056f23df2b74b91fede0bbbfa

std.Thread.Mutex: restore the "Held" API

so that std.Thread.Mutex.Dummy can be used as a drop in replacement.

2 files changed, 94 insertions(+), 78 deletions(-)

lib/std/Thread/Mutex.zig+91-75
...@@ -38,30 +38,16 @@ const linux = os.linux;...@@ -38,30 +38,16 @@ const linux = os.linux;
38const testing = std.testing;38const testing = std.testing;
39const StaticResetEvent = std.thread.StaticResetEvent;39const StaticResetEvent = std.thread.StaticResetEvent;
4040
41pub 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`.
4343pub 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.
52pub fn tryAcquire(m: *Mutex) ?Held {
53 if (m.impl.tryAcquire()) {
54 return Held{ .impl = &m.impl };
55 } else {
56 return null;
57 }
58}45}
5946
60/// Acquire the mutex. Deadlocks if the mutex is already47/// Acquire the mutex. Deadlocks if the mutex is already
61/// held by the calling thread.48/// held by the calling thread.
62pub fn acquire(m: *Mutex) Held {49pub fn acquire(m: *Mutex) Impl.Held {
63 m.impl.acquire();50 return m.impl.acquire();
64 return .{ .impl = &m.impl };
65}51}
6652
67const Impl = if (builtin.single_threaded)53const 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 };
8470
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 }
9597
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 }
102105
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;
106109
...@@ -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 {
128131
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 }
153156
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);
164159
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 {
182pub const PthreadMutex = struct {177pub const PthreadMutex = struct {
183 pthread_mutex: std.c.pthread_mutex_t = .{},178 pthread_mutex: std.c.pthread_mutex_t = .{},
184179
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 if194 /// Try to acquire the mutex without blocking. Returns null if
186 /// the mutex is unavailable. Otherwise returns Held. Call195 /// 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 }
191204
192 /// Acquire the mutex. Will deadlock if the mutex is already205 /// 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};
216219
217/// This has the sematics as `Mutex`, however it does not actually do any220/// 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 {
221224
222 const lock_init = if (std.debug.runtime_safety) false else {};225 const lock_init = if (std.debug.runtime_safety) false else {};
223226
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 if237 /// Try to acquire the mutex without blocking. Returns null if
225 /// the mutex is unavailable. Otherwise returns Held. Call238 /// 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 }
234247
235 /// Acquire the mutex. Will deadlock if the mutex is already248 /// 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};
247254
248const WindowsMutex = struct {255const WindowsMutex = struct {
249 srwlock: windows.SRWLOCK = windows.SRWLOCK_INIT,256 srwlock: windows.SRWLOCK = windows.SRWLOCK_INIT,
250257
251 pub fn tryAcquire(self: *WindowsMutex) bool {258 pub const Held = struct {
252 return TryAcquireSRWLockExclusive(&self.srwlock) != system.FALSE;259 mutex: *WindowsMutex,
253 }
254260
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 }
258273
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};
263279
lib/std/heap/general_purpose_allocator.zig+3-3
...@@ -150,12 +150,12 @@ pub const Config = struct {...@@ -150,12 +150,12 @@ pub const Config = struct {
150150
151 /// What type of mutex you'd like to use, for thread safety.151 /// What type of mutex you'd like to use, for thread safety.
152 /// when specfied, the mutex type must have the same shape as `std.Thread.Mutex` and152 /// when specfied, the mutex type must have the same shape as `std.Thread.Mutex` and
153 /// `std.mutex.Dummy`, and have no required fields. Specifying this field causes153 /// `std.Thread.Mutex.Dummy`, and have no required fields. Specifying this field causes
154 /// the `thread_safe` field to be ignored.154 /// the `thread_safe` field to be ignored.
155 ///155 ///
156 /// when null (default):156 /// when null (default):
157 /// * the mutex type defaults to `std.Thread.Mutex` when thread_safe is enabled.157 /// * the mutex type defaults to `std.Thread.Mutex` when thread_safe is enabled.
158 /// * the mutex type defaults to `std.mutex.Dummy` otherwise.158 /// * the mutex type defaults to `std.Thread.Mutex.Dummy` otherwise.
159 MutexType: ?type = null,159 MutexType: ?type = null,
160160
161 /// This is a temporary debugging trick you can use to turn segfaults into more helpful161 /// This is a temporary debugging trick you can use to turn segfaults into more helpful
...@@ -189,7 +189,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -189,7 +189,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
189 else if (config.thread_safe)189 else if (config.thread_safe)
190 std.Thread.Mutex{}190 std.Thread.Mutex{}
191 else191 else
192 std.mutex.Dummy{};192 std.Thread.Mutex.Dummy{};
193193
194 const stack_n = config.stack_trace_frames;194 const stack_n = config.stack_trace_frames;
195 const one_trace_size = @sizeOf(usize) * stack_n;195 const one_trace_size = @sizeOf(usize) * stack_n;