Mutex is a synchronization primitive which enforces atomic access to a shared region of code known as the "critical section".
Mutex is an extern struct so that it may be used as a field inside another extern struct.
pub const Mutex = extern struct
pub const Mutex = extern struct {
state: std.atomic.Value(State),
pub const init: Mutex = .{ .state = .init(.unlocked) };
pub const State = enum(u32) {
unlocked,
locked_once,
contended,
};
pub fn tryLock(m: *Mutex) bool {
return m.state.cmpxchgStrong(.unlocked, .locked_once, .acquire, .monotonic) == null;
}
pub fn lock(m: *Mutex, io: Io) Cancelable!void {
const initial_state = m.state.cmpxchgStrong(
.unlocked,
.locked_once,
.acquire,
.monotonic,
) orelse {
@branchHint(.likely);
return;
};
if (initial_state == .contended) {
try io.futexWait(State, &m.state.raw, .contended);
}
while (m.state.swap(.contended, .acquire) != .unlocked) {
try io.futexWait(State, &m.state.raw, .contended);
}
}
/// Same as `lock`, except does not introduce a cancelation point.
///
/// For a description of cancelation and cancelation points, see `Future.cancel`.
pub fn lockUncancelable(m: *Mutex, io: Io) void {
const initial_state = m.state.cmpxchgStrong(
.unlocked,
.locked_once,
.acquire,
.monotonic,
) orelse {
@branchHint(.likely);
return;
};
if (initial_state == .contended) {
io.futexWaitUncancelable(State, &m.state.raw, .contended);
}
while (m.state.swap(.contended, .acquire) != .unlocked) {
io.futexWaitUncancelable(State, &m.state.raw, .contended);
}
}
pub fn unlock(m: *Mutex, io: Io) void {
switch (m.state.swap(.unlocked, .release)) {
.unlocked => unreachable,
.locked_once => {},
.contended => {
@branchHint(.unlikely);
io.futexWake(State, &m.state.raw, 1);
},
}
}
}