diff --git a/lib/std/os/windows/kernel32.zig b/lib/std/os/windows/kernel32.zig index 736d81ae58..d466f9946d 100644 --- a/lib/std/os/windows/kernel32.zig +++ b/lib/std/os/windows/kernel32.zig @@ -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; diff --git a/lib/std/spinlock.zig b/lib/std/spinlock.zig index 905460a2d0..894acd0fc4 100644 --- a/lib/std/spinlock.zig +++ b/lib/std/spinlock.zig @@ -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" {