Skip to content

Commit a2732fc

Browse files
committed
std.mutex: implement blocking mutexes on Linux
see #1455 Eventually I would like to see something like WTF::Lock/WTF::ParkingLot, but that is a bunch of work.
1 parent d596808 commit a2732fc

File tree

1 file changed

+40
-4
lines changed

1 file changed

+40
-4
lines changed

std/mutex.zig

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,31 @@ const builtin = @import("builtin");
33
const AtomicOrder = builtin.AtomicOrder;
44
const AtomicRmwOp = builtin.AtomicRmwOp;
55
const assert = std.debug.assert;
6+
const linux = std.os.linux;
7+
8+
// Reading: Futexes Are Tricky by Ulrich Drepper https://www.akkadia.org/drepper/futex.pdf
9+
10+
// TODO robust mutexes https://www.kernel.org/doc/Documentation/robust-futexes.txt
611

7-
/// TODO use syscalls instead of a spinlock
812
pub const Mutex = struct {
9-
lock: u8, // TODO use a bool
13+
// TODO: Windows and OSX with futex equivilents
14+
// 0: unlocked
15+
// 1: locked, no waiters
16+
// 2: locked: one or more waiters
17+
lock: u32, // futexs are 32-bits on all architectures
1018

1119
pub const Held = struct {
1220
mutex: *Mutex,
1321

1422
pub fn release(self: Held) void {
15-
assert(@atomicRmw(u8, &self.mutex.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
23+
if (@atomicRmw(u32, &self.mutex.lock, AtomicRmwOp.Sub, 1, AtomicOrder.Release) != 1) {
24+
self.mutex.lock = 0;
25+
if (builtin.os == builtin.Os.linux) {
26+
_ = linux.futex_wake(@ptrToInt(&self.mutex.lock), linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, 1);
27+
} else {
28+
@compileError("not implemented");
29+
}
30+
}
1631
}
1732
};
1833

@@ -21,7 +36,28 @@ pub const Mutex = struct {
2136
}
2237

2338
pub fn acquire(self: *Mutex) Held {
24-
while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
39+
var c: u32 = undefined;
40+
// This need not be strong because of the loop that follows.
41+
// TODO implement mutex3 from https://www.akkadia.org/drepper/futex.pdf in x86 assembly.
42+
if (@cmpxchgWeak(u32, &self.lock, 0, 1, AtomicOrder.Acquire, AtomicOrder.Monotonic)) |value1| {
43+
c = value1;
44+
while (true) {
45+
if (c == 2 or
46+
@cmpxchgWeak(u32, &self.lock, 1, 2, AtomicOrder.Acquire, AtomicOrder.Monotonic) == null)
47+
{
48+
if (builtin.os == builtin.Os.linux) {
49+
_ = linux.futex_wait(@ptrToInt(&self.lock), linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG, 2, null);
50+
} else {
51+
@compileError("not implemented");
52+
}
53+
}
54+
if (@cmpxchgWeak(u32, &self.lock, 0, 2, AtomicOrder.Acquire, AtomicOrder.Monotonic)) |value2| {
55+
c = value2;
56+
} else {
57+
break;
58+
}
59+
}
60+
}
2561
return Held{ .mutex = self };
2662
}
2763
};

0 commit comments

Comments
 (0)