mirror of
https://github.com/ziglang/zig.git
synced 2024-11-27 07:32:44 +00:00
SpinLock: loopHint & yield distinction
This commit is contained in:
parent
26e08d5701
commit
c912296443
@ -75,7 +75,7 @@ else if (builtin.os == .windows)
|
||||
|
||||
fn acquireSlow(self: *Mutex) Held {
|
||||
@setCold(true);
|
||||
while (true) : (SpinLock.yield(1)) {
|
||||
while (true) : (SpinLock.loopHint(1)) {
|
||||
const waiters = @atomicLoad(u32, &self.waiters, .Monotonic);
|
||||
|
||||
// try and take lock if unlocked
|
||||
@ -99,7 +99,7 @@ else if (builtin.os == .windows)
|
||||
// unlock without a rmw/cmpxchg instruction
|
||||
@atomicStore(u8, @ptrCast(*u8, &self.mutex.locked), 0, .Release);
|
||||
|
||||
while (true) : (SpinLock.yield(1)) {
|
||||
while (true) : (SpinLock.loopHint(1)) {
|
||||
const waiters = @atomicLoad(u32, &self.mutex.waiters, .Monotonic);
|
||||
|
||||
// no one is waiting
|
||||
@ -142,10 +142,6 @@ else if (builtin.link_libc or builtin.os == .linux)
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
fn yield() void {
|
||||
os.sched_yield() catch SpinLock.yield(30);
|
||||
}
|
||||
|
||||
pub fn tryAcquire(self: *Mutex) ?Held {
|
||||
if (@cmpxchgWeak(usize, &self.state, 0, MUTEX_LOCK, .Acquire, .Monotonic) != null)
|
||||
return null;
|
||||
@ -175,7 +171,7 @@ else if (builtin.link_libc or builtin.os == .linux)
|
||||
} else if (state & QUEUE_MASK == 0) {
|
||||
break;
|
||||
}
|
||||
yield();
|
||||
SpinLock.yield();
|
||||
state = @atomicLoad(usize, &self.state, .Monotonic);
|
||||
}
|
||||
|
||||
@ -198,7 +194,7 @@ else if (builtin.link_libc or builtin.os == .linux)
|
||||
break;
|
||||
};
|
||||
}
|
||||
yield();
|
||||
SpinLock.yield();
|
||||
state = @atomicLoad(usize, &self.state, .Monotonic);
|
||||
}
|
||||
}
|
||||
@ -225,7 +221,7 @@ else if (builtin.link_libc or builtin.os == .linux)
|
||||
// try and lock the LFIO queue to pop a node off,
|
||||
// stopping altogether if its already locked or the queue is empty
|
||||
var state = @atomicLoad(usize, &self.state, .Monotonic);
|
||||
while (true) : (std.SpinLock.yield(1)) {
|
||||
while (true) : (SpinLock.loopHint(1)) {
|
||||
if (state & QUEUE_LOCK != 0 or state & QUEUE_MASK == 0)
|
||||
return;
|
||||
state = @cmpxchgWeak(usize, &self.state, state, state | QUEUE_LOCK, .Acquire, .Monotonic) orelse break;
|
||||
@ -234,7 +230,7 @@ else if (builtin.link_libc or builtin.os == .linux)
|
||||
// acquired the QUEUE_LOCK, try and pop a node to wake it.
|
||||
// if the mutex is locked, then unset QUEUE_LOCK and let
|
||||
// the thread who holds the mutex do the wake-up on unlock()
|
||||
while (true) : (std.SpinLock.yield(1)) {
|
||||
while (true) : (SpinLock.loopHint(1)) {
|
||||
if ((state & MUTEX_LOCK) != 0) {
|
||||
state = @cmpxchgWeak(usize, &self.state, state, state & ~QUEUE_LOCK, .Release, .Acquire) orelse return;
|
||||
} else {
|
||||
|
@ -234,10 +234,7 @@ const AtomicEvent = struct {
|
||||
timer = time.Timer.start() catch unreachable;
|
||||
|
||||
while (@atomicLoad(i32, ptr, .Acquire) == expected) {
|
||||
switch (builtin.os) {
|
||||
.windows => SpinLock.yield(400),
|
||||
else => os.sched_yield() catch SpinLock.yield(1),
|
||||
}
|
||||
SpinLock.yield();
|
||||
if (timeout) |timeout_ns| {
|
||||
if (timer.read() >= timeout_ns)
|
||||
return error.TimedOut;
|
||||
@ -320,7 +317,7 @@ const AtomicEvent = struct {
|
||||
return @intToPtr(?windows.HANDLE, handle);
|
||||
},
|
||||
LOADING => {
|
||||
SpinLock.yield(1000);
|
||||
SpinLock.yield();
|
||||
handle = @atomicLoad(usize, &event_handle, .Monotonic);
|
||||
},
|
||||
else => {
|
||||
|
@ -35,27 +35,33 @@ pub const SpinLock = struct {
|
||||
pub fn acquire(self: *SpinLock) Held {
|
||||
while (true) {
|
||||
return self.tryAcquire() orelse {
|
||||
// On native windows, SwitchToThread is too expensive,
|
||||
// and yielding for 380-410 iterations was found to be
|
||||
// a nice sweet spot. Posix systems on the other hand,
|
||||
// especially linux, perform better by yielding the thread.
|
||||
switch (builtin.os) {
|
||||
.windows => yield(400),
|
||||
else => std.os.sched_yield() catch yield(1),
|
||||
}
|
||||
yield();
|
||||
continue;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn yield() void {
|
||||
// On native windows, SwitchToThread is too expensive,
|
||||
// and yielding for 380-410 iterations was found to be
|
||||
// a nice sweet spot. Posix systems on the other hand,
|
||||
// especially linux, perform better by yielding the thread.
|
||||
switch (builtin.os) {
|
||||
.windows => loopHint(400),
|
||||
else => std.os.sched_yield() catch loopHint(1),
|
||||
}
|
||||
}
|
||||
|
||||
/// Hint to the cpu that execution is spinning
|
||||
/// for the given amount of iterations.
|
||||
pub fn yield(iterations: usize) void {
|
||||
pub fn loopHint(iterations: usize) void {
|
||||
var i = iterations;
|
||||
while (i != 0) : (i -= 1) {
|
||||
switch (builtin.arch) {
|
||||
.i386, .x86_64 => asm volatile ("pause"),
|
||||
.arm, .aarch64 => asm volatile ("yield"),
|
||||
// these instructions use a memory clobber as they
|
||||
// flush the pipeline of any speculated reads/writes.
|
||||
.i386, .x86_64 => asm volatile ("pause" ::: "memory"),
|
||||
.arm, .aarch64 => asm volatile ("yield" ::: "memory"),
|
||||
else => std.os.sched_yield() catch {},
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user