| author | |
| committer | |
| log | b535e86cc0e86e76471aebd731566ae140098a07 |
| tree | 36e49cab7ff4c229228980f7c541abd4ad2997b2 |
| parent | 92dac89d019fcd91aa043a116d6a6e166da8c4b2 |
5 files changed, 39 insertions(+), 30 deletions(-)
lib/std/c.zig+1| ... | ... | @@ -158,6 +158,7 @@ pub extern "c" fn pthread_attr_init(attr: *pthread_attr_t) c_int; |
| 158 | 158 | pub extern "c" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: *c_void, stacksize: usize) c_int; |
| 159 | 159 | pub extern "c" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int; |
| 160 | 160 | pub extern "c" fn pthread_self() pthread_t; |
| 161 | pub extern "c" fn pthread_yield() c_int; | |
| 161 | 162 | pub extern "c" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int; |
| 162 | 163 | |
| 163 | 164 | pub extern "c" fn kqueue() c_int; |
lib/std/mutex.zig+9-8| ... | ... | @@ -46,11 +46,11 @@ else struct { |
| 46 | 46 | const Locked = 2; |
| 47 | 47 | |
| 48 | 48 | /// number of iterations to spin yielding the cpu |
| 49 | const SpinCpu = 4; | |
| 49 | const SPIN_CPU = 4; | |
| 50 | 50 | /// number of iterations to perform in the cpu yield loop |
| 51 | const SpinCpuCount = 30; | |
| 51 | const SPIN_CPU_COUNT = 30; | |
| 52 | 52 | /// number of iterations to spin yielding the thread |
| 53 | const SpinThread = 1; | |
| 53 | const SPIN_THREAD = 1; | |
| 54 | 54 | |
| 55 | 55 | pub fn init() Mutex { |
| 56 | 56 | return Mutex{ |
| ... | ... | @@ -86,20 +86,21 @@ else struct { |
| 86 | 86 | |
| 87 | 87 | while (true) { |
| 88 | 88 | // try and acquire the lock using cpu spinning on failure |
| 89 | for (([SpinCpu]void)(undefined)) |_| { | |
| 89 | var spin: usize = 0; | |
| 90 | while (spin < SPIN_CPU) : (spin += 1) { | |
| 90 | 91 | var value = @atomicLoad(u32, &self.state, .Monotonic); |
| 91 | 92 | while (value == Unlocked) |
| 92 | 93 | value = @cmpxchgWeak(u32, &self.state, Unlocked, state, .Acquire, .Monotonic) orelse return Held{ .mutex = self }; |
| 93 | for (([SpinCpuCount]void)(undefined)) |_| | |
| 94 | SpinLock.yieldCpu(); | |
| 94 | SpinLock.yield(SPIN_CPU_COUNT); | |
| 95 | 95 | } |
| 96 | 96 | |
| 97 | 97 | // try and acquire the lock using thread rescheduling on failure |
| 98 | for (([SpinThread]void)(undefined)) |_| { | |
| 98 | spin = 0; | |
| 99 | while (spin < SPIN_THREAD) : (spin += 1) { | |
| 99 | 100 | var value = @atomicLoad(u32, &self.state, .Monotonic); |
| 100 | 101 | while (value == Unlocked) |
| 101 | 102 | value = @cmpxchgWeak(u32, &self.state, Unlocked, state, .Acquire, .Monotonic) orelse return Held{ .mutex = self }; |
| 102 | SpinLock.yieldThread(); | |
| 103 | std.os.yield(); | |
| 103 | 104 | } |
| 104 | 105 | |
| 105 | 106 | // failed to acquire the lock, go to sleep until woken up by `Held.release()` |
lib/std/os.zig+10| ... | ... | @@ -3169,3 +3169,13 @@ pub fn dn_expand( |
| 3169 | 3169 | } |
| 3170 | 3170 | return error.InvalidDnsPacket; |
| 3171 | 3171 | } |
| 3172 | ||
| 3173 | pub fn yield() void { | |
| 3174 | switch (builtin.os) { | |
| 3175 | .windows => _ = windows.kernel32.SwitchToThread(), | |
| 3176 | .linux => _ = assert(linux.sched_yield() == 0), | |
| 3177 | else => if (builtin.link_libc) { | |
| 3178 | assert(std.c.pthread_yield() == 0); | |
| 3179 | }, | |
| 3180 | } | |
| 3181 | } |
lib/std/os/linux.zig+4| ... | ... | @@ -954,6 +954,10 @@ pub fn fremovexattr(fd: usize, name: [*]const u8) usize { |
| 954 | 954 | return syscall2(SYS_fremovexattr, fd, @ptrToInt(name)); |
| 955 | 955 | } |
| 956 | 956 | |
| 957 | pub fn sched_yield() usize { | |
| 958 | return syscall0(SYS_sched_yield); | |
| 959 | } | |
| 960 | ||
| 957 | 961 | pub fn sched_getaffinity(pid: i32, size: usize, set: *cpu_set_t) usize { |
| 958 | 962 | const rc = syscall3(SYS_sched_getaffinity, @bitCast(usize, isize(pid)), size, @ptrToInt(set)); |
| 959 | 963 | if (@bitCast(isize, rc) < 0) return rc; |
lib/std/spinlock.zig+15-22| ... | ... | @@ -28,22 +28,17 @@ pub const SpinLock = struct { |
| 28 | 28 | return Held{ .spinlock = self }; |
| 29 | 29 | } |
| 30 | 30 | |
| 31 | pub fn yieldCpu() void { | |
| 32 | switch (builtin.arch) { | |
| 33 | .i386, .x86_64 => asm volatile("pause" ::: "memory"), | |
| 34 | // .arm, .aarch64 => asm volatile("yield"), | |
| 35 | // | |
| 36 | // Causes CI to fail | |
| 37 | // See: https://github.com/ziglang/zig/pull/3585#issuecomment-549962765 | |
| 38 | else => time.sleep(0), | |
| 39 | } | |
| 40 | } | |
| 41 | ||
| 42 | pub fn yieldThread() void { | |
| 43 | switch (builtin.os) { | |
| 44 | .linux => assert(linux.syscall0(linux.SYS_sched_yield) == 0), | |
| 45 | .windows => _ = windows.kernel32.SwitchToThread(), | |
| 46 | else => time.sleep(1 * time.microsecond), | |
| 31 | pub fn yield(iterations: usize) void { | |
| 32 | var i = iterations; | |
| 33 | while (i != 0) : (i -= 1) { | |
| 34 | switch (builtin.arch) { | |
| 35 | .i386, .x86_64 => asm volatile("pause" ::: "memory"), | |
| 36 | // .arm, .aarch64 => asm volatile("yield"), | |
| 37 | // | |
| 38 | // Causes CI to fail | |
| 39 | // See: https://github.com/ziglang/zig/pull/3585#issuecomment-549962765 | |
| 40 | else => time.sleep(0), | |
| 41 | } | |
| 47 | 42 | } |
| 48 | 43 | } |
| 49 | 44 | |
| ... | ... | @@ -55,16 +50,14 @@ pub const SpinLock = struct { |
| 55 | 50 | return @This(){ .iteration = 0 }; |
| 56 | 51 | } |
| 57 | 52 | |
| 58 | /// Hybrid yielding from | |
| 53 | /// Modified hybrid yielding from | |
| 59 | 54 | /// http://www.1024cores.net/home/lock-free-algorithms/tricks/spinning |
| 60 | 55 | pub fn yield(self: *@This()) void { |
| 61 | 56 | defer self.iteration +%= 1; |
| 62 | if (self.iteration < 10) { | |
| 63 | yieldCpu(); | |
| 64 | } else if (self.iteration < 20) { | |
| 65 | for (([30]void)(undefined)) |_| yieldCpu(); | |
| 57 | if (self.iteration < 20) { | |
| 58 | SpinLock.yield(self.iteration); | |
| 66 | 59 | } else if (self.iteration < 24) { |
| 67 | yieldThread(); | |
| 60 | os.yield(); | |
| 68 | 61 | } else if (self.iteration < 26) { |
| 69 | 62 | time.sleep(1 * time.millisecond); |
| 70 | 63 | } else { |