| author | |
| committer | |
| log | c9122964436b16dd44a5fb8dfd92f0768ad6fef3 |
| tree | 43a293be46eef6d84725cea2e86b9ce5759f7a61 |
| parent | 26e08d57016352c469ae34b3fbd61e7f2a85a76c |
3 files changed, 25 insertions(+), 26 deletions(-)
lib/std/mutex.zig+6-10| ... | ... | @@ -75,7 +75,7 @@ else if (builtin.os == .windows) |
| 75 | 75 | |
| 76 | 76 | fn acquireSlow(self: *Mutex) Held { |
| 77 | 77 | @setCold(true); |
| 78 | while (true) : (SpinLock.yield(1)) { | |
| 78 | while (true) : (SpinLock.loopHint(1)) { | |
| 79 | 79 | const waiters = @atomicLoad(u32, &self.waiters, .Monotonic); |
| 80 | 80 | |
| 81 | 81 | // try and take lock if unlocked |
| ... | ... | @@ -99,7 +99,7 @@ else if (builtin.os == .windows) |
| 99 | 99 | // unlock without a rmw/cmpxchg instruction |
| 100 | 100 | @atomicStore(u8, @ptrCast(*u8, &self.mutex.locked), 0, .Release); |
| 101 | 101 | |
| 102 | while (true) : (SpinLock.yield(1)) { | |
| 102 | while (true) : (SpinLock.loopHint(1)) { | |
| 103 | 103 | const waiters = @atomicLoad(u32, &self.mutex.waiters, .Monotonic); |
| 104 | 104 | |
| 105 | 105 | // no one is waiting |
| ... | ... | @@ -142,10 +142,6 @@ else if (builtin.link_libc or builtin.os == .linux) |
| 142 | 142 | self.* = undefined; |
| 143 | 143 | } |
| 144 | 144 | |
| 145 | fn yield() void { | |
| 146 | os.sched_yield() catch SpinLock.yield(30); | |
| 147 | } | |
| 148 | ||
| 149 | 145 | pub fn tryAcquire(self: *Mutex) ?Held { |
| 150 | 146 | if (@cmpxchgWeak(usize, &self.state, 0, MUTEX_LOCK, .Acquire, .Monotonic) != null) |
| 151 | 147 | return null; |
| ... | ... | @@ -175,7 +171,7 @@ else if (builtin.link_libc or builtin.os == .linux) |
| 175 | 171 | } else if (state & QUEUE_MASK == 0) { |
| 176 | 172 | break; |
| 177 | 173 | } |
| 178 | yield(); | |
| 174 | SpinLock.yield(); | |
| 179 | 175 | state = @atomicLoad(usize, &self.state, .Monotonic); |
| 180 | 176 | } |
| 181 | 177 | |
| ... | ... | @@ -198,7 +194,7 @@ else if (builtin.link_libc or builtin.os == .linux) |
| 198 | 194 | break; |
| 199 | 195 | }; |
| 200 | 196 | } |
| 201 | yield(); | |
| 197 | SpinLock.yield(); | |
| 202 | 198 | state = @atomicLoad(usize, &self.state, .Monotonic); |
| 203 | 199 | } |
| 204 | 200 | } |
| ... | ... | @@ -225,7 +221,7 @@ else if (builtin.link_libc or builtin.os == .linux) |
| 225 | 221 | // try and lock the LFIO queue to pop a node off, |
| 226 | 222 | // stopping altogether if its already locked or the queue is empty |
| 227 | 223 | var state = @atomicLoad(usize, &self.state, .Monotonic); |
| 228 | while (true) : (std.SpinLock.yield(1)) { | |
| 224 | while (true) : (SpinLock.loopHint(1)) { | |
| 229 | 225 | if (state & QUEUE_LOCK != 0 or state & QUEUE_MASK == 0) |
| 230 | 226 | return; |
| 231 | 227 | state = @cmpxchgWeak(usize, &self.state, state, state | QUEUE_LOCK, .Acquire, .Monotonic) orelse break; |
| ... | ... | @@ -234,7 +230,7 @@ else if (builtin.link_libc or builtin.os == .linux) |
| 234 | 230 | // acquired the QUEUE_LOCK, try and pop a node to wake it. |
| 235 | 231 | // if the mutex is locked, then unset QUEUE_LOCK and let |
| 236 | 232 | // the thread who holds the mutex do the wake-up on unlock() |
| 237 | while (true) : (std.SpinLock.yield(1)) { | |
| 233 | while (true) : (SpinLock.loopHint(1)) { | |
| 238 | 234 | if ((state & MUTEX_LOCK) != 0) { |
| 239 | 235 | state = @cmpxchgWeak(usize, &self.state, state, state & ~QUEUE_LOCK, .Release, .Acquire) orelse return; |
| 240 | 236 | } else { |
lib/std/reset_event.zig+2-5| ... | ... | @@ -234,10 +234,7 @@ const AtomicEvent = struct { |
| 234 | 234 | timer = time.Timer.start() catch unreachable; |
| 235 | 235 | |
| 236 | 236 | while (@atomicLoad(i32, ptr, .Acquire) == expected) { |
| 237 | switch (builtin.os) { | |
| 238 | .windows => SpinLock.yield(400), | |
| 239 | else => os.sched_yield() catch SpinLock.yield(1), | |
| 240 | } | |
| 237 | SpinLock.yield(); | |
| 241 | 238 | if (timeout) |timeout_ns| { |
| 242 | 239 | if (timer.read() >= timeout_ns) |
| 243 | 240 | return error.TimedOut; |
| ... | ... | @@ -320,7 +317,7 @@ const AtomicEvent = struct { |
| 320 | 317 | return @intToPtr(?windows.HANDLE, handle); |
| 321 | 318 | }, |
| 322 | 319 | LOADING => { |
| 323 | SpinLock.yield(1000); | |
| 320 | SpinLock.yield(); | |
| 324 | 321 | handle = @atomicLoad(usize, &event_handle, .Monotonic); |
| 325 | 322 | }, |
| 326 | 323 | else => { |
lib/std/spinlock.zig+17-11| ... | ... | @@ -35,27 +35,33 @@ pub const SpinLock = struct { |
| 35 | 35 | pub fn acquire(self: *SpinLock) Held { |
| 36 | 36 | while (true) { |
| 37 | 37 | return self.tryAcquire() orelse { |
| 38 | // On native windows, SwitchToThread is too expensive, | |
| 39 | // and yielding for 380-410 iterations was found to be | |
| 40 | // a nice sweet spot. Posix systems on the other hand, | |
| 41 | // especially linux, perform better by yielding the thread. | |
| 42 | switch (builtin.os) { | |
| 43 | .windows => yield(400), | |
| 44 | else => std.os.sched_yield() catch yield(1), | |
| 45 | } | |
| 38 | yield(); | |
| 46 | 39 | continue; |
| 47 | 40 | }; |
| 48 | 41 | } |
| 49 | 42 | } |
| 50 | 43 | |
| 44 | pub fn yield() void { | |
| 45 | // On native windows, SwitchToThread is too expensive, | |
| 46 | // and yielding for 380-410 iterations was found to be | |
| 47 | // a nice sweet spot. Posix systems on the other hand, | |
| 48 | // especially linux, perform better by yielding the thread. | |
| 49 | switch (builtin.os) { | |
| 50 | .windows => loopHint(400), | |
| 51 | else => std.os.sched_yield() catch loopHint(1), | |
| 52 | } | |
| 53 | } | |
| 54 | ||
| 51 | 55 | /// Hint to the cpu that execution is spinning |
| 52 | 56 | /// for the given amount of iterations. |
| 53 | pub fn yield(iterations: usize) void { | |
| 57 | pub fn loopHint(iterations: usize) void { | |
| 54 | 58 | var i = iterations; |
| 55 | 59 | while (i != 0) : (i -= 1) { |
| 56 | 60 | switch (builtin.arch) { |
| 57 | .i386, .x86_64 => asm volatile ("pause"), | |
| 58 | .arm, .aarch64 => asm volatile ("yield"), | |
| 61 | // these instructions use a memory clobber as they | |
| 62 | // flush the pipeline of any speculated reads/writes. | |
| 63 | .i386, .x86_64 => asm volatile ("pause" ::: "memory"), | |
| 64 | .arm, .aarch64 => asm volatile ("yield" ::: "memory"), | |
| 59 | 65 | else => std.os.sched_yield() catch {}, |
| 60 | 66 | } |
| 61 | 67 | } |