| ... | @@ -1,8 +1,9 @@ | ... | @@ -1,8 +1,9 @@ |
| 1 | const std = @import("std.zig"); | 1 | const std = @import("std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const AtomicOrder = builtin.AtomicOrder; | | |
| 4 | const AtomicRmwOp = builtin.AtomicRmwOp; | | |
| 5 | const assert = std.debug.assert; | 3 | const assert = std.debug.assert; |
| | 4 | const time = std.time; |
| | 5 | const linux = std.os.linux; |
| | 6 | const windows = std.os.windows; |
| 6 | | 7 | |
| 7 | pub const SpinLock = struct { | 8 | pub const SpinLock = struct { |
| 8 | lock: u8, // TODO use a bool or enum | 9 | lock: u8, // TODO use a bool or enum |
| ... | @@ -11,7 +12,8 @@ pub const SpinLock = struct { | ... | @@ -11,7 +12,8 @@ pub const SpinLock = struct { |
| 11 | spinlock: *SpinLock, | 12 | spinlock: *SpinLock, |
| 12 | | 13 | |
| 13 | pub fn release(self: Held) void { | 14 | pub fn release(self: Held) void { |
| 14 | assert(@atomicRmw(u8, &self.spinlock.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1); | 15 | // TODO: @atomicStore() https://github.com/ziglang/zig/issues/2995 |
| | 16 | assert(@atomicRmw(u8, &self.spinlock.lock, .Xchg, 0, .Release) == 1); |
| 15 | } | 17 | } |
| 16 | }; | 18 | }; |
| 17 | | 19 | |
| ... | @@ -19,10 +21,41 @@ pub const SpinLock = struct { | ... | @@ -19,10 +21,41 @@ pub const SpinLock = struct { |
| 19 | return SpinLock{ .lock = 0 }; | 21 | return SpinLock{ .lock = 0 }; |
| 20 | } | 22 | } |
| 21 | | 23 | |
| | 24 | // Hybrid spinning from |
| | 25 | // http://www.1024cores.net/home/lock-free-algorithms/tricks/spinning |
| 22 | pub fn acquire(self: *SpinLock) Held { | 26 | pub fn acquire(self: *SpinLock) Held { |
| 23 | while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {} | 27 | var backoff: usize = 0; |
| | 28 | while (@atomicRmw(u8, &self.lock, .Xchg, 1, .Acquire) != 0) : (backoff +%= 1) { |
| | 29 | if (backoff < 10) { |
| | 30 | yieldCpu(); |
| | 31 | } else if (backoff < 20) { |
| | 32 | for (([30]void)(undefined)) |_| yieldCpu(); |
| | 33 | } else if (backoff < 24) { |
| | 34 | yieldThread(); |
| | 35 | } else if (backoff < 26) { |
| | 36 | time.sleep(1 * time.millisecond); |
| | 37 | } else { |
| | 38 | time.sleep(10 * time.millisecond); |
| | 39 | } |
| | 40 | } |
| 24 | return Held{ .spinlock = self }; | 41 | return Held{ .spinlock = self }; |
| 25 | } | 42 | } |
| | 43 | |
| | 44 | fn yieldCpu() void { |
| | 45 | switch (builtin.arch) { |
| | 46 | .i386, .x86_64 => asm volatile("pause" ::: "memory"), |
| | 47 | .arm, .aarch64 => asm volatile("yield"), |
| | 48 | else => time.sleep(0), |
| | 49 | } |
| | 50 | } |
| | 51 | |
| | 52 | fn yieldThread() void { |
| | 53 | switch (builtin.os) { |
| | 54 | .linux => assert(linux.syscall0(linux.SYS_sched_yield) == 0), |
| | 55 | .windows => _ = windows.kernel32.SwitchToThread(), |
| | 56 | else => time.sleep(1 * time.microsecond), |
| | 57 | } |
| | 58 | } |
| 26 | }; | 59 | }; |
| 27 | | 60 | |
| 28 | test "spinlock" { | 61 | test "spinlock" { |