mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-03-08 04:24:33 +01:00
std: halve the number of mutexes per mutex
On NetBSD and Illumos, we were using the `std.Thread.Futex`-based implementation of `std.Thread.Mutex`. But since futex is not a primitive on these targets, the implementation of `std.Thread.Futex` was based on pthread primitives, including `pthread_mutex_t`. This had the amusing consequence that locking a contended mutex on NetBSD would actually perform 2 mutex locks, 2 mutex unlocks, and 1 condition wait; likewise, unlocking a contended mutex would perform 2 mutex locks, 2 mutex unlocks, and 1 condition signal. Having read some cutting-edge studies, I have concluded that this is a slightly suboptimal approach. Instead, let's just use pthread mutexes directly in this case; that's an obviously better idea. In the future, I think we can probably entirely remove our usages of pthread sync primitives---no platform actually treats them as the base primitives. Of the platforms which std has any meaningful support for today, most support futexes, and the exceptions (NetBSD and Illumos) support a thread parking API. We can implement futex and/or mutex on top of thread parking and drop the pthread dependency entirely.
This commit is contained in:
parent
c518593e97
commit
4f16e80cea
1 changed files with 55 additions and 8 deletions
|
|
@ -45,14 +45,30 @@ const Impl = if (builtin.mode == .Debug and !builtin.single_threaded)
|
|||
else
|
||||
ReleaseImpl;
|
||||
|
||||
const ReleaseImpl = if (builtin.single_threaded)
|
||||
SingleThreadedImpl
|
||||
else if (builtin.os.tag == .windows)
|
||||
WindowsImpl
|
||||
else if (builtin.os.tag.isDarwin())
|
||||
DarwinImpl
|
||||
else
|
||||
FutexImpl;
|
||||
const ReleaseImpl = Impl: {
|
||||
if (builtin.single_threaded) break :Impl SingleThreadedImpl;
|
||||
if (builtin.os.tag == .windows) break :Impl WindowsImpl;
|
||||
if (builtin.os.tag.isDarwin()) break :Impl DarwinImpl;
|
||||
|
||||
if (builtin.target.os.tag == .linux or
|
||||
builtin.target.os.tag == .freebsd or
|
||||
builtin.target.os.tag == .openbsd or
|
||||
builtin.target.os.tag == .dragonfly or
|
||||
builtin.target.cpu.arch.isWasm())
|
||||
{
|
||||
// Futex is the system's synchronization primitive; use that.
|
||||
break :Impl FutexImpl;
|
||||
}
|
||||
|
||||
if (std.Thread.use_pthreads) {
|
||||
// This system doesn't have a futex primitive, so `std.Thread.Futex` is using `PosixImpl`,
|
||||
// which implements futex *on top of* pthread mutexes and conditions. Therefore, instead
|
||||
// of going through that long inefficient path, just use pthread mutex directly.
|
||||
break :Impl PosixImpl;
|
||||
}
|
||||
|
||||
break :Impl FutexImpl;
|
||||
};
|
||||
|
||||
const DebugImpl = struct {
|
||||
locking_thread: std.atomic.Value(Thread.Id) = std.atomic.Value(Thread.Id).init(0), // 0 means it's not locked.
|
||||
|
|
@ -208,6 +224,37 @@ const FutexImpl = struct {
|
|||
}
|
||||
};
|
||||
|
||||
const PosixImpl = struct {
|
||||
mutex: std.c.pthread_mutex_t = .{},
|
||||
|
||||
fn tryLock(impl: *PosixImpl) bool {
|
||||
switch (std.c.pthread_mutex_trylock(&impl.mutex)) {
|
||||
.SUCCESS => return true,
|
||||
.BUSY => return false,
|
||||
.INVAL => unreachable, // mutex is initialized correctly
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn lock(impl: *PosixImpl) void {
|
||||
switch (std.c.pthread_mutex_lock(&impl.mutex)) {
|
||||
.SUCCESS => return,
|
||||
.INVAL => unreachable, // mutex is initialized correctly
|
||||
.DEADLK => unreachable, // not an error checking mutex
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn unlock(impl: *PosixImpl) void {
|
||||
switch (std.c.pthread_mutex_unlock(&impl.mutex)) {
|
||||
.SUCCESS => return,
|
||||
.INVAL => unreachable, // mutex is initialized correctly
|
||||
.PERM => unreachable, // not an error checking mutex
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
test "smoke test" {
|
||||
var mutex = Mutex{};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue