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;
3838const testing = std.testing;
3939const StaticResetEvent = std.thread.StaticResetEvent;
4040
41pub 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.
52pub 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`.
43pub fn tryAcquire(m: *Mutex) ?Impl.Held {
44 return m.impl.tryAcquire();
5845}
5946
6047/// Acquire the mutex. Deadlocks if the mutex is already
6148/// held by the calling thread.
62pub fn acquire(m: *Mutex) Held {
63 m.impl.acquire();
64 return .{ .impl = &m.impl };
49pub fn acquire(m: *Mutex) Impl.Held {
50 return m.impl.acquire();
6551}
6652
6753const Impl = if (builtin.single_threaded)
......@@ -82,25 +68,42 @@ pub const AtomicMutex = struct {
8268 waiting,
8369 };
8470
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(
8785 State,
88 &self.state,
86 &m.state,
8987 .unlocked,
9088 .locked,
9189 .Acquire,
9290 .Monotonic,
93 ) == null;
91 ) == null) {
92 return Held{ .mutex = m };
93 } else {
94 return null;
95 }
9496 }
9597
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)) {
98100 .unlocked => {},
99 else => |s| self.lockSlow(s),
101 else => |s| m.lockSlow(s),
100102 }
103 return Held{ .mutex = m };
101104 }
102105
103 fn lockSlow(self: *AtomicMutex, current_state: State) void {
106 fn lockSlow(m: *AtomicMutex, current_state: State) void {
104107 @setCold(true);
105108 var new_state = current_state;
106109
......@@ -108,7 +111,7 @@ pub const AtomicMutex = struct {
108111 while (spin < 100) : (spin += 1) {
109112 const state = @cmpxchgWeak(
110113 State,
111 &self.state,
114 &m.state,
112115 .unlocked,
113116 new_state,
114117 .Acquire,
......@@ -128,14 +131,14 @@ pub const AtomicMutex = struct {
128131
129132 new_state = .waiting;
130133 while (true) {
131 switch (@atomicRmw(State, &self.state, .Xchg, new_state, .Acquire)) {
134 switch (@atomicRmw(State, &m.state, .Xchg, new_state, .Acquire)) {
132135 .unlocked => return,
133136 else => {},
134137 }
135138 switch (std.Target.current.os.tag) {
136139 .linux => {
137140 switch (linux.getErrno(linux.futex_wait(
138 @ptrCast(*const i32, &self.state),
141 @ptrCast(*const i32, &m.state),
139142 linux.FUTEX_PRIVATE_FLAG | linux.FUTEX_WAIT,
140143 @enumToInt(new_state),
141144 null,
......@@ -151,21 +154,13 @@ pub const AtomicMutex = struct {
151154 }
152155 }
153156
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 {
163158 @setCold(true);
164159
165160 switch (std.Target.current.os.tag) {
166161 .linux => {
167162 switch (linux.getErrno(linux.futex_wake(
168 @ptrCast(*const i32, &self.state),
163 @ptrCast(*const i32, &m.state),
169164 linux.FUTEX_PRIVATE_FLAG | linux.FUTEX_WAKE,
170165 1,
171166 ))) {
......@@ -182,18 +177,36 @@ pub const AtomicMutex = struct {
182177pub const PthreadMutex = struct {
183178 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
185194 /// Try to acquire the mutex without blocking. Returns null if
186195 /// the mutex is unavailable. Otherwise returns Held. Call
187196 /// 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 }
190203 }
191204
192205 /// Acquire the mutex. Will deadlock if the mutex is already
193206 /// 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 },
197210 std.c.EINVAL => unreachable,
198211 std.c.EBUSY => unreachable,
199212 std.c.EAGAIN => unreachable,
......@@ -202,16 +215,6 @@ pub const PthreadMutex = struct {
202215 else => unreachable,
203216 }
204217 }
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 }
215218};
216219
217220/// This has the sematics as `Mutex`, however it does not actually do any
......@@ -221,43 +224,56 @@ pub const Dummy = struct {
221224
222225 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
224237 /// Try to acquire the mutex without blocking. Returns null if
225238 /// the mutex is unavailable. Otherwise returns Held. Call
226239 /// release on Held.
227 pub fn tryAcquire(self: *Dummy) bool {
240 pub fn tryAcquire(m: *Dummy) ?Held {
228241 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;
231244 }
232 return true;
245 return Held{ .mutex = m };
233246 }
234247
235248 /// Acquire the mutex. Will deadlock if the mutex is already
236249 /// 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");
245252 }
246253};
247254
248255const WindowsMutex = struct {
249256 srwlock: windows.SRWLOCK = windows.SRWLOCK_INIT,
250257
251 pub fn tryAcquire(self: *WindowsMutex) bool {
252 return TryAcquireSRWLockExclusive(&self.srwlock) != system.FALSE;
253 }
258 pub const Held = struct {
259 mutex: *WindowsMutex,
254260
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 }
257272 }
258273
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 };
261277 }
262278};
263279
lib/std/heap/general_purpose_allocator.zig+3-3
......@@ -150,12 +150,12 @@ pub const Config = struct {
150150
151151 /// What type of mutex you'd like to use, for thread safety.
152152 /// 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 causes
153 /// `std.Thread.Mutex.Dummy`, and have no required fields. Specifying this field causes
154154 /// the `thread_safe` field to be ignored.
155155 ///
156156 /// when null (default):
157157 /// * 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.
159159 MutexType: ?type = null,
160160
161161 /// 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 {
189189 else if (config.thread_safe)
190190 std.Thread.Mutex{}
191191 else
192 std.mutex.Dummy{};
192 std.Thread.Mutex.Dummy{};
193193
194194 const stack_n = config.stack_trace_frames;
195195 const one_trace_size = @sizeOf(usize) * stack_n;