From b535e86cc0e86e76471aebd731566ae140098a07 Mon Sep 17 00:00:00 2001 From: kprotty Date: Thu, 7 Nov 2019 15:32:20 -0600 Subject: [PATCH] move SpinLock definitions around --- lib/std/c.zig | 1 + lib/std/mutex.zig | 17 +++++++++-------- lib/std/os.zig | 10 ++++++++++ lib/std/os/linux.zig | 4 ++++ lib/std/spinlock.zig | 37 +++++++++++++++---------------------- 5 files changed, 39 insertions(+), 30 deletions(-) diff --git a/lib/std/c.zig b/lib/std/c.zig index 71e111a61d..201aa6b103 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -158,6 +158,7 @@ pub extern "c" fn pthread_attr_init(attr: *pthread_attr_t) c_int; pub extern "c" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: *c_void, stacksize: usize) c_int; pub extern "c" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int; pub extern "c" fn pthread_self() pthread_t; +pub extern "c" fn pthread_yield() c_int; pub extern "c" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int; pub extern "c" fn kqueue() c_int; diff --git a/lib/std/mutex.zig b/lib/std/mutex.zig index 26f0142c7c..110f581f5e 100644 --- a/lib/std/mutex.zig +++ b/lib/std/mutex.zig @@ -46,11 +46,11 @@ else struct { const Locked = 2; /// number of iterations to spin yielding the cpu - const SpinCpu = 4; + const SPIN_CPU = 4; /// number of iterations to perform in the cpu yield loop - const SpinCpuCount = 30; + const SPIN_CPU_COUNT = 30; /// number of iterations to spin yielding the thread - const SpinThread = 1; + const SPIN_THREAD = 1; pub fn init() Mutex { return Mutex{ @@ -86,20 +86,21 @@ else struct { while (true) { // try and acquire the lock using cpu spinning on failure - for (([SpinCpu]void)(undefined)) |_| { + var spin: usize = 0; + while (spin < SPIN_CPU) : (spin += 1) { var value = @atomicLoad(u32, &self.state, .Monotonic); while (value == Unlocked) value = @cmpxchgWeak(u32, &self.state, Unlocked, state, .Acquire, .Monotonic) orelse return Held{ .mutex = self }; - for (([SpinCpuCount]void)(undefined)) |_| - SpinLock.yieldCpu(); + SpinLock.yield(SPIN_CPU_COUNT); } // try and acquire the lock using thread rescheduling on failure - for (([SpinThread]void)(undefined)) |_| { + spin = 0; + while (spin < SPIN_THREAD) : (spin += 1) { var value = @atomicLoad(u32, &self.state, .Monotonic); while (value == Unlocked) value = @cmpxchgWeak(u32, &self.state, Unlocked, state, .Acquire, .Monotonic) orelse return Held{ .mutex = self }; - SpinLock.yieldThread(); + std.os.yield(); } // failed to acquire the lock, go to sleep until woken up by `Held.release()` diff --git a/lib/std/os.zig b/lib/std/os.zig index efddc9aafb..72dd2a7196 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -3169,3 +3169,13 @@ pub fn dn_expand( } return error.InvalidDnsPacket; } + +pub fn yield() void { + switch (builtin.os) { + .windows => _ = windows.kernel32.SwitchToThread(), + .linux => _ = assert(linux.sched_yield() == 0), + else => if (builtin.link_libc) { + assert(std.c.pthread_yield() == 0); + }, + } +} diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index d9f1f4e311..a7dedefb43 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -954,6 +954,10 @@ pub fn fremovexattr(fd: usize, name: [*]const u8) usize { return syscall2(SYS_fremovexattr, fd, @ptrToInt(name)); } +pub fn sched_yield() usize { + return syscall0(SYS_sched_yield); +} + pub fn sched_getaffinity(pid: i32, size: usize, set: *cpu_set_t) usize { const rc = syscall3(SYS_sched_getaffinity, @bitCast(usize, isize(pid)), size, @ptrToInt(set)); if (@bitCast(isize, rc) < 0) return rc; diff --git a/lib/std/spinlock.zig b/lib/std/spinlock.zig index dadf96cef5..55b92f51d1 100644 --- a/lib/std/spinlock.zig +++ b/lib/std/spinlock.zig @@ -28,22 +28,17 @@ pub const SpinLock = struct { return Held{ .spinlock = self }; } - pub fn yieldCpu() void { - switch (builtin.arch) { - .i386, .x86_64 => asm volatile("pause" ::: "memory"), - // .arm, .aarch64 => asm volatile("yield"), - // - // Causes CI to fail - // See: https://github.com/ziglang/zig/pull/3585#issuecomment-549962765 - else => time.sleep(0), - } - } - - pub 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), + pub fn yield(iterations: usize) void { + var i = iterations; + while (i != 0) : (i -= 1) { + switch (builtin.arch) { + .i386, .x86_64 => asm volatile("pause" ::: "memory"), + // .arm, .aarch64 => asm volatile("yield"), + // + // Causes CI to fail + // See: https://github.com/ziglang/zig/pull/3585#issuecomment-549962765 + else => time.sleep(0), + } } } @@ -55,16 +50,14 @@ pub const SpinLock = struct { return @This(){ .iteration = 0 }; } - /// Hybrid yielding from + /// Modified hybrid yielding from /// http://www.1024cores.net/home/lock-free-algorithms/tricks/spinning pub fn yield(self: *@This()) void { defer self.iteration +%= 1; - if (self.iteration < 10) { - yieldCpu(); - } else if (self.iteration < 20) { - for (([30]void)(undefined)) |_| yieldCpu(); + if (self.iteration < 20) { + SpinLock.yield(self.iteration); } else if (self.iteration < 24) { - yieldThread(); + os.yield(); } else if (self.iteration < 26) { time.sleep(1 * time.millisecond); } else {