authorgravatar for kbutcher6200@gmail.comkprotty <kbutcher6200@gmail.com> 2019-12-17 08:57:07-06:00
committergravatar for kbutcher6200@gmail.comkprotty <kbutcher6200@gmail.com> 2019-12-17 15:38:00-06:00
logc9122964436b16dd44a5fb8dfd92f0768ad6fef3
tree43a293be46eef6d84725cea2e86b9ce5759f7a61
parent26e08d57016352c469ae34b3fbd61e7f2a85a76c

SpinLock: loopHint & yield distinction


3 files changed, 25 insertions(+), 26 deletions(-)

lib/std/mutex.zig+6-10
......@@ -75,7 +75,7 @@ else if (builtin.os == .windows)
7575
7676 fn acquireSlow(self: *Mutex) Held {
7777 @setCold(true);
78 while (true) : (SpinLock.yield(1)) {
78 while (true) : (SpinLock.loopHint(1)) {
7979 const waiters = @atomicLoad(u32, &self.waiters, .Monotonic);
8080
8181 // try and take lock if unlocked
......@@ -99,7 +99,7 @@ else if (builtin.os == .windows)
9999 // unlock without a rmw/cmpxchg instruction
100100 @atomicStore(u8, @ptrCast(*u8, &self.mutex.locked), 0, .Release);
101101
102 while (true) : (SpinLock.yield(1)) {
102 while (true) : (SpinLock.loopHint(1)) {
103103 const waiters = @atomicLoad(u32, &self.mutex.waiters, .Monotonic);
104104
105105 // no one is waiting
......@@ -142,10 +142,6 @@ else if (builtin.link_libc or builtin.os == .linux)
142142 self.* = undefined;
143143 }
144144
145 fn yield() void {
146 os.sched_yield() catch SpinLock.yield(30);
147 }
148
149145 pub fn tryAcquire(self: *Mutex) ?Held {
150146 if (@cmpxchgWeak(usize, &self.state, 0, MUTEX_LOCK, .Acquire, .Monotonic) != null)
151147 return null;
......@@ -175,7 +171,7 @@ else if (builtin.link_libc or builtin.os == .linux)
175171 } else if (state & QUEUE_MASK == 0) {
176172 break;
177173 }
178 yield();
174 SpinLock.yield();
179175 state = @atomicLoad(usize, &self.state, .Monotonic);
180176 }
181177
......@@ -198,7 +194,7 @@ else if (builtin.link_libc or builtin.os == .linux)
198194 break;
199195 };
200196 }
201 yield();
197 SpinLock.yield();
202198 state = @atomicLoad(usize, &self.state, .Monotonic);
203199 }
204200 }
......@@ -225,7 +221,7 @@ else if (builtin.link_libc or builtin.os == .linux)
225221 // try and lock the LFIO queue to pop a node off,
226222 // stopping altogether if its already locked or the queue is empty
227223 var state = @atomicLoad(usize, &self.state, .Monotonic);
228 while (true) : (std.SpinLock.yield(1)) {
224 while (true) : (SpinLock.loopHint(1)) {
229225 if (state & QUEUE_LOCK != 0 or state & QUEUE_MASK == 0)
230226 return;
231227 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)
234230 // acquired the QUEUE_LOCK, try and pop a node to wake it.
235231 // if the mutex is locked, then unset QUEUE_LOCK and let
236232 // 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)) {
238234 if ((state & MUTEX_LOCK) != 0) {
239235 state = @cmpxchgWeak(usize, &self.state, state, state & ~QUEUE_LOCK, .Release, .Acquire) orelse return;
240236 } else {
lib/std/reset_event.zig+2-5
......@@ -234,10 +234,7 @@ const AtomicEvent = struct {
234234 timer = time.Timer.start() catch unreachable;
235235
236236 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();
241238 if (timeout) |timeout_ns| {
242239 if (timer.read() >= timeout_ns)
243240 return error.TimedOut;
......@@ -320,7 +317,7 @@ const AtomicEvent = struct {
320317 return @intToPtr(?windows.HANDLE, handle);
321318 },
322319 LOADING => {
323 SpinLock.yield(1000);
320 SpinLock.yield();
324321 handle = @atomicLoad(usize, &event_handle, .Monotonic);
325322 },
326323 else => {
lib/std/spinlock.zig+17-11
......@@ -35,27 +35,33 @@ pub const SpinLock = struct {
3535 pub fn acquire(self: *SpinLock) Held {
3636 while (true) {
3737 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();
4639 continue;
4740 };
4841 }
4942 }
5043
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
5155 /// Hint to the cpu that execution is spinning
5256 /// for the given amount of iterations.
53 pub fn yield(iterations: usize) void {
57 pub fn loopHint(iterations: usize) void {
5458 var i = iterations;
5559 while (i != 0) : (i -= 1) {
5660 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"),
5965 else => std.os.sched_yield() catch {},
6066 }
6167 }