adaptive SpinLock
parent
bb6ad1a6c2
commit
0d4f4aad9e
|
@ -184,6 +184,8 @@ pub extern "kernel32" stdcallcc fn SetHandleInformation(hObject: HANDLE, dwMask:
|
|||
|
||||
pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD) void;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn SwitchToThread() BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn TlsAlloc() DWORD;
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
const std = @import("std.zig");
|
||||
const builtin = @import("builtin");
|
||||
const AtomicOrder = builtin.AtomicOrder;
|
||||
const AtomicRmwOp = builtin.AtomicRmwOp;
|
||||
const assert = std.debug.assert;
|
||||
const time = std.time;
|
||||
const linux = std.os.linux;
|
||||
const windows = std.os.windows;
|
||||
|
||||
pub const SpinLock = struct {
|
||||
lock: u8, // TODO use a bool or enum
|
||||
|
@ -11,7 +12,8 @@ pub const SpinLock = struct {
|
|||
spinlock: *SpinLock,
|
||||
|
||||
pub fn release(self: Held) void {
|
||||
assert(@atomicRmw(u8, &self.spinlock.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
|
||||
// TODO: @atomicStore() https://github.com/ziglang/zig/issues/2995
|
||||
assert(@atomicRmw(u8, &self.spinlock.lock, .Xchg, 0, .Release) == 1);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -19,10 +21,41 @@ pub const SpinLock = struct {
|
|||
return SpinLock{ .lock = 0 };
|
||||
}
|
||||
|
||||
// Hybrid spinning from
|
||||
// http://www.1024cores.net/home/lock-free-algorithms/tricks/spinning
|
||||
pub fn acquire(self: *SpinLock) Held {
|
||||
while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
|
||||
var backoff: usize = 0;
|
||||
while (@atomicRmw(u8, &self.lock, .Xchg, 1, .Acquire) != 0) : (backoff +%= 1) {
|
||||
if (backoff < 10) {
|
||||
yieldCpu();
|
||||
} else if (backoff < 20) {
|
||||
for (([30]void)(undefined)) |_| yieldCpu();
|
||||
} else if (backoff < 24) {
|
||||
yieldThread();
|
||||
} else if (backoff < 26) {
|
||||
time.sleep(1 * time.millisecond);
|
||||
} else {
|
||||
time.sleep(10 * time.millisecond);
|
||||
}
|
||||
}
|
||||
return Held{ .spinlock = self };
|
||||
}
|
||||
|
||||
fn yieldCpu() void {
|
||||
switch (builtin.arch) {
|
||||
.i386, .x86_64 => asm volatile("pause" ::: "memory"),
|
||||
.arm, .aarch64 => asm volatile("yield"),
|
||||
else => time.sleep(0),
|
||||
}
|
||||
}
|
||||
|
||||
fn yieldThread() void {
|
||||
switch (builtin.os) {
|
||||
.linux => assert(linux.syscall0(linux.SYS_sched_yield) == 0),
|
||||
.windows => _ = windows.kernel32.SwitchToThread(),
|
||||
else => time.sleep(1 * time.microsecond),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
test "spinlock" {
|
||||
|
|
Loading…
Reference in New Issue