Commit 0d4f4aad9e

kprotty <kbutcher6200@gmail.com>
2019-11-05 15:16:08
adaptive SpinLock
1 parent bb6ad1a
Changed files (2)
lib
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;
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" {