Commit c912296443

kprotty <kbutcher6200@gmail.com>
2019-12-17 15:57:07
SpinLock: loopHint & yield distinction
1 parent 26e08d5
Changed files (3)
lib/std/mutex.zig
@@ -75,7 +75,7 @@ else if (builtin.os == .windows)
 
         fn acquireSlow(self: *Mutex) Held {
             @setCold(true);
-            while (true) : (SpinLock.yield(1)) {
+            while (true) : (SpinLock.loopHint(1)) {
                 const waiters = @atomicLoad(u32, &self.waiters, .Monotonic);
 
                 // try and take lock if unlocked
@@ -99,7 +99,7 @@ else if (builtin.os == .windows)
                 // unlock without a rmw/cmpxchg instruction
                 @atomicStore(u8, @ptrCast(*u8, &self.mutex.locked), 0, .Release);
 
-                while (true) : (SpinLock.yield(1)) {
+                while (true) : (SpinLock.loopHint(1)) {
                     const waiters = @atomicLoad(u32, &self.mutex.waiters, .Monotonic);
                 
                     // no one is waiting
@@ -142,10 +142,6 @@ else if (builtin.link_libc or builtin.os == .linux)
             self.* = undefined;
         }
 
-        fn yield() void {
-            os.sched_yield() catch SpinLock.yield(30);
-        }
-
         pub fn tryAcquire(self: *Mutex) ?Held {
             if (@cmpxchgWeak(usize, &self.state, 0, MUTEX_LOCK, .Acquire, .Monotonic) != null)
                 return null;
@@ -175,7 +171,7 @@ else if (builtin.link_libc or builtin.os == .linux)
                     } else if (state & QUEUE_MASK == 0) {
                         break;
                     }
-                    yield();
+                    SpinLock.yield();
                     state = @atomicLoad(usize, &self.state, .Monotonic);
                 }
 
@@ -198,7 +194,7 @@ else if (builtin.link_libc or builtin.os == .linux)
                             break;
                         };
                     }
-                    yield();
+                    SpinLock.yield();
                     state = @atomicLoad(usize, &self.state, .Monotonic);
                 }
             }
@@ -225,7 +221,7 @@ else if (builtin.link_libc or builtin.os == .linux)
             // try and lock the LFIO queue to pop a node off,
             // stopping altogether if its already locked or the queue is empty
             var state = @atomicLoad(usize, &self.state, .Monotonic);
-            while (true) : (std.SpinLock.yield(1)) {
+            while (true) : (SpinLock.loopHint(1)) {
                 if (state & QUEUE_LOCK != 0 or state & QUEUE_MASK == 0)
                     return;
                 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)
             // acquired the QUEUE_LOCK, try and pop a node to wake it.
             // if the mutex is locked, then unset QUEUE_LOCK and let
             // the thread who holds the mutex do the wake-up on unlock()
-            while (true) : (std.SpinLock.yield(1)) {
+            while (true) : (SpinLock.loopHint(1)) {
                 if ((state & MUTEX_LOCK) != 0) {
                     state = @cmpxchgWeak(usize, &self.state, state, state & ~QUEUE_LOCK, .Release, .Acquire) orelse return;
                 } else {
lib/std/reset_event.zig
@@ -234,10 +234,7 @@ const AtomicEvent = struct {
                 timer = time.Timer.start() catch unreachable;
 
             while (@atomicLoad(i32, ptr, .Acquire) == expected) {
-                switch (builtin.os) {
-                    .windows => SpinLock.yield(400),
-                    else => os.sched_yield() catch SpinLock.yield(1),
-                }
+                SpinLock.yield();
                 if (timeout) |timeout_ns| {
                     if (timer.read() >= timeout_ns)
                         return error.TimedOut;
@@ -320,7 +317,7 @@ const AtomicEvent = struct {
                         return @intToPtr(?windows.HANDLE, handle);
                     },
                     LOADING => {
-                        SpinLock.yield(1000);
+                        SpinLock.yield();
                         handle = @atomicLoad(usize, &event_handle, .Monotonic);
                     },
                     else => {
lib/std/spinlock.zig
@@ -35,27 +35,33 @@ pub const SpinLock = struct {
     pub fn acquire(self: *SpinLock) Held {
         while (true) {
             return self.tryAcquire() orelse {
-                // On native windows, SwitchToThread is too expensive,
-                // and yielding for 380-410 iterations was found to be
-                // a nice sweet spot. Posix systems on the other hand,
-                // especially linux, perform better by yielding the thread.
-                switch (builtin.os) {
-                    .windows => yield(400),
-                    else => std.os.sched_yield() catch yield(1),
-                }
+                yield();
                 continue;
             };
         }
     }
 
+    pub fn yield() void {
+        // On native windows, SwitchToThread is too expensive,
+        // and yielding for 380-410 iterations was found to be
+        // a nice sweet spot. Posix systems on the other hand,
+        // especially linux, perform better by yielding the thread.
+        switch (builtin.os) {
+            .windows => loopHint(400),
+            else => std.os.sched_yield() catch loopHint(1),
+        }
+    }
+
     /// Hint to the cpu that execution is spinning
     /// for the given amount of iterations.
-    pub fn yield(iterations: usize) void {
+    pub fn loopHint(iterations: usize) void {
         var i = iterations;
         while (i != 0) : (i -= 1) {
             switch (builtin.arch) {
-                .i386, .x86_64 => asm volatile ("pause"),
-                .arm, .aarch64 => asm volatile ("yield"),
+                // these instructions use a memory clobber as they
+                // flush the pipeline of any speculated reads/writes.
+                .i386, .x86_64 => asm volatile ("pause" ::: "memory"),
+                .arm, .aarch64 => asm volatile ("yield" ::: "memory"),
                 else => std.os.sched_yield() catch {},
             }
         }