mirror of
https://github.com/ziglang/zig.git
synced 2026-01-20 14:25:16 +00:00
std.Thread.Mutex: restore the "Held" API
so that std.Thread.Mutex.Dummy can be used as a drop in replacement.
This commit is contained in:
parent
a9667b5a85
commit
9698ea3173
@ -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;
|
||||
}
|
||||
|
||||
pub fn acquire(self: *AtomicMutex) void {
|
||||
switch (@atomicRmw(State, &self.state, .Xchg, .locked, .Acquire)) {
|
||||
.unlocked => {},
|
||||
else => |s| self.lockSlow(s),
|
||||
) == null) {
|
||||
return Held{ .mutex = m };
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
fn lockSlow(self: *AtomicMutex, current_state: State) void {
|
||||
pub fn acquire(m: *AtomicMutex) Held {
|
||||
switch (@atomicRmw(State, &m.state, .Xchg, .locked, .Acquire)) {
|
||||
.unlocked => {},
|
||||
else => |s| m.lockSlow(s),
|
||||
}
|
||||
return Held{ .mutex = m };
|
||||
}
|
||||
|
||||
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 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 acquire(self: *WindowsMutex) void {
|
||||
AcquireSRWLockExclusive(&self.srwlock);
|
||||
}
|
||||
|
||||
pub fn release(self: *WindowsMutex) void {
|
||||
ReleaseSRWLockExclusive(&self.srwlock);
|
||||
pub fn acquire(m: *WindowsMutex) Held {
|
||||
windows.AcquireSRWLockExclusive(&m.srwlock);
|
||||
return Held{ .mutex = m };
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user