Commit 9698ea3173
Changed files (2)
lib
std
Thread
lib/std/heap/general_purpose_allocator.zig
@@ -150,12 +150,12 @@ pub const Config = struct {
/// What type of mutex you'd like to use, for thread safety.
/// when specfied, the mutex type must have the same shape as `std.Thread.Mutex` and
- /// `std.mutex.Dummy`, and have no required fields. Specifying this field causes
+ /// `std.Thread.Mutex.Dummy`, and have no required fields. Specifying this field causes
/// the `thread_safe` field to be ignored.
///
/// when null (default):
/// * the mutex type defaults to `std.Thread.Mutex` when thread_safe is enabled.
- /// * the mutex type defaults to `std.mutex.Dummy` otherwise.
+ /// * the mutex type defaults to `std.Thread.Mutex.Dummy` otherwise.
MutexType: ?type = null,
/// 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 {
else if (config.thread_safe)
std.Thread.Mutex{}
else
- std.mutex.Dummy{};
+ std.Thread.Mutex.Dummy{};
const stack_n = config.stack_trace_frames;
const one_trace_size = @sizeOf(usize) * stack_n;
lib/std/Thread/Mutex.zig
@@ -38,30 +38,16 @@ const linux = os.linux;
const testing = std.testing;
const StaticResetEvent = std.thread.StaticResetEvent;
-pub const Held = struct {
- impl: *Impl,
-
- pub fn release(held: Held) void {
- held.impl.release();
- }
-};
-
-/// Try to acquire the mutex without blocking. Returns null if
-/// the mutex is unavailable. Otherwise returns Held. Call
-/// release on Held.
-pub fn tryAcquire(m: *Mutex) ?Held {
- if (m.impl.tryAcquire()) {
- return Held{ .impl = &m.impl };
- } else {
- return null;
- }
+/// Try to acquire the mutex without blocking. Returns `null` if the mutex is
+/// unavailable. Otherwise returns `Held`. Call `release` on `Held`.
+pub fn tryAcquire(m: *Mutex) ?Impl.Held {
+ return m.impl.tryAcquire();
}
/// Acquire the mutex. Deadlocks if the mutex is already
/// held by the calling thread.
-pub fn acquire(m: *Mutex) Held {
- m.impl.acquire();
- return .{ .impl = &m.impl };
+pub fn acquire(m: *Mutex) Impl.Held {
+ return m.impl.acquire();
}
const Impl = if (builtin.single_threaded)
@@ -82,25 +68,42 @@ pub const AtomicMutex = struct {
waiting,
};
- pub fn tryAcquire(self: *AtomicMutex) bool {
- return @cmpxchgStrong(
+ pub const Held = struct {
+ mutex: *AtomicMutex,
+
+ pub fn release(held: Held) void {
+ switch (@atomicRmw(State, &held.mutex.state, .Xchg, .unlocked, .Release)) {
+ .unlocked => unreachable,
+ .locked => {},
+ .waiting => held.mutex.unlockSlow(),
+ }
+ }
+ };
+
+ pub fn tryAcquire(m: *AtomicMutex) ?Held {
+ if (@cmpxchgStrong(
State,
- &self.state,
+ &m.state,
.unlocked,
.locked,
.Acquire,
.Monotonic,
- ) == null;
+ ) == null) {
+ return Held{ .mutex = m };
+ } else {
+ return null;
+ }
}
- pub fn acquire(self: *AtomicMutex) void {
- switch (@atomicRmw(State, &self.state, .Xchg, .locked, .Acquire)) {
+ pub fn acquire(m: *AtomicMutex) Held {
+ switch (@atomicRmw(State, &m.state, .Xchg, .locked, .Acquire)) {
.unlocked => {},
- else => |s| self.lockSlow(s),
+ else => |s| m.lockSlow(s),
}
+ return Held{ .mutex = m };
}
- fn lockSlow(self: *AtomicMutex, current_state: State) void {
+ fn lockSlow(m: *AtomicMutex, current_state: State) void {
@setCold(true);
var new_state = current_state;
@@ -108,7 +111,7 @@ pub const AtomicMutex = struct {
while (spin < 100) : (spin += 1) {
const state = @cmpxchgWeak(
State,
- &self.state,
+ &m.state,
.unlocked,
new_state,
.Acquire,
@@ -128,14 +131,14 @@ pub const AtomicMutex = struct {
new_state = .waiting;
while (true) {
- switch (@atomicRmw(State, &self.state, .Xchg, new_state, .Acquire)) {
+ switch (@atomicRmw(State, &m.state, .Xchg, new_state, .Acquire)) {
.unlocked => return,
else => {},
}
switch (std.Target.current.os.tag) {
.linux => {
switch (linux.getErrno(linux.futex_wait(
- @ptrCast(*const i32, &self.state),
+ @ptrCast(*const i32, &m.state),
linux.FUTEX_PRIVATE_FLAG | linux.FUTEX_WAIT,
@enumToInt(new_state),
null,
@@ -151,21 +154,13 @@ pub const AtomicMutex = struct {
}
}
- pub fn release(self: *AtomicMutex) void {
- switch (@atomicRmw(State, &self.state, .Xchg, .unlocked, .Release)) {
- .unlocked => unreachable,
- .locked => {},
- .waiting => self.unlockSlow(),
- }
- }
-
- fn unlockSlow(self: *AtomicMutex) void {
+ fn unlockSlow(m: *AtomicMutex) void {
@setCold(true);
switch (std.Target.current.os.tag) {
.linux => {
switch (linux.getErrno(linux.futex_wake(
- @ptrCast(*const i32, &self.state),
+ @ptrCast(*const i32, &m.state),
linux.FUTEX_PRIVATE_FLAG | linux.FUTEX_WAKE,
1,
))) {
@@ -182,18 +177,36 @@ pub const AtomicMutex = struct {
pub const PthreadMutex = struct {
pthread_mutex: std.c.pthread_mutex_t = .{},
+ pub const Held = struct {
+ mutex: *PthreadMutex,
+
+ pub fn release(held: Held) void {
+ switch (std.c.pthread_mutex_unlock(&held.mutex.pthread_mutex)) {
+ 0 => return,
+ std.c.EINVAL => unreachable,
+ std.c.EAGAIN => unreachable,
+ std.c.EPERM => unreachable,
+ else => unreachable,
+ }
+ }
+ };
+
/// Try to acquire the mutex without blocking. Returns null if
/// the mutex is unavailable. Otherwise returns Held. Call
/// release on Held.
- pub fn tryAcquire(self: *PthreadMutex) bool {
- return std.c.pthread_mutex_trylock(&self.pthread_mutex) == 0;
+ pub fn tryAcquire(m: *PthreadMutex) ?Held {
+ if (std.c.pthread_mutex_trylock(&m.pthread_mutex) == 0) {
+ return Held{ .mutex = m };
+ } else {
+ return null;
+ }
}
/// Acquire the mutex. Will deadlock if the mutex is already
/// held by the calling thread.
- pub fn acquire(self: *PthreadMutex) void {
- switch (std.c.pthread_mutex_lock(&self.pthread_mutex)) {
- 0 => return,
+ pub fn acquire(m: *PthreadMutex) Held {
+ switch (std.c.pthread_mutex_lock(&m.pthread_mutex)) {
+ 0 => return Held{ .mutex = m },
std.c.EINVAL => unreachable,
std.c.EBUSY => unreachable,
std.c.EAGAIN => unreachable,
@@ -202,16 +215,6 @@ pub const PthreadMutex = struct {
else => unreachable,
}
}
-
- pub fn release(self: *PthreadMutex) void {
- switch (std.c.pthread_mutex_unlock(&self.pthread_mutex)) {
- 0 => return,
- std.c.EINVAL => unreachable,
- std.c.EAGAIN => unreachable,
- std.c.EPERM => unreachable,
- else => unreachable,
- }
- }
};
/// This has the sematics as `Mutex`, however it does not actually do any
@@ -221,43 +224,56 @@ pub const Dummy = struct {
const lock_init = if (std.debug.runtime_safety) false else {};
+ pub const Held = struct {
+ mutex: *Dummy,
+
+ pub fn release(held: Held) void {
+ if (std.debug.runtime_safety) {
+ held.mutex.lock = false;
+ }
+ }
+ };
+
/// Try to acquire the mutex without blocking. Returns null if
/// the mutex is unavailable. Otherwise returns Held. Call
/// release on Held.
- pub fn tryAcquire(self: *Dummy) bool {
+ pub fn tryAcquire(m: *Dummy) ?Held {
if (std.debug.runtime_safety) {
- if (self.lock) return false;
- self.lock = true;
+ if (m.lock) return null;
+ m.lock = true;
}
- return true;
+ return Held{ .mutex = m };
}
/// Acquire the mutex. Will deadlock if the mutex is already
/// held by the calling thread.
- pub fn acquire(self: *Dummy) void {
- return self.tryAcquire() orelse @panic("deadlock detected");
- }
-
- pub fn release(self: *Dummy) void {
- if (std.debug.runtime_safety) {
- self.mutex.lock = false;
- }
+ pub fn acquire(m: *Dummy) Held {
+ return m.tryAcquire() orelse @panic("deadlock detected");
}
};
const WindowsMutex = struct {
srwlock: windows.SRWLOCK = windows.SRWLOCK_INIT,
- pub fn tryAcquire(self: *WindowsMutex) bool {
- return TryAcquireSRWLockExclusive(&self.srwlock) != system.FALSE;
- }
+ pub const Held = struct {
+ mutex: *WindowsMutex,
- pub fn acquire(self: *WindowsMutex) void {
- AcquireSRWLockExclusive(&self.srwlock);
+ pub fn release(held: Held) void {
+ windows.ReleaseSRWLockExclusive(&held.mutex.srwlock);
+ }
+ };
+
+ pub fn tryAcquire(m: *WindowsMutex) ?Held {
+ if (windows.TryAcquireSRWLockExclusive(&m.srwlock) != windows.FALSE) {
+ return Held{ .mutex = m };
+ } else {
+ return null;
+ }
}
- pub fn release(self: *WindowsMutex) void {
- ReleaseSRWLockExclusive(&self.srwlock);
+ pub fn acquire(m: *WindowsMutex) Held {
+ windows.AcquireSRWLockExclusive(&m.srwlock);
+ return Held{ .mutex = m };
}
};