1 /** 2 * SpinLock for runtime internal usage. 3 * 4 * Copyright: Copyright Digital Mars 2015 -. 5 * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 6 * Authors: Martin Nowak 7 * Source: $(DRUNTIMESRC core/internal/_spinlock.d) 8 */ 9 module core.internal.spinlock; 10 11 import core.atomic, core.thread; 12 13 shared struct SpinLock 14 { 15 /// for how long is the lock usually contended 16 enum Contention : ubyte 17 { 18 brief, 19 medium, 20 lengthy, 21 } 22 23 @trusted @nogc nothrow: thisSpinLock24 this(Contention contention) 25 { 26 this.contention = contention; 27 } 28 lockSpinLock29 void lock() 30 { 31 if (cas(&val, size_t(0), size_t(1))) 32 return; 33 // Try to reduce the chance of another cas failure 34 // TTAS lock (https://en.wikipedia.org/wiki/Test_and_test-and-set) 35 immutable step = 1 << contention; 36 while (true) 37 { 38 for (size_t n; atomicLoad!(MemoryOrder.raw)(val); n += step) 39 yield(n); 40 if (cas(&val, size_t(0), size_t(1))) 41 return; 42 } 43 } 44 unlockSpinLock45 void unlock() 46 { 47 atomicStore!(MemoryOrder.rel)(val, size_t(0)); 48 } 49 50 /// yield with backoff yieldSpinLock51 void yield(size_t k) 52 { 53 import core.time; 54 if (k < pauseThresh) 55 return core.atomic.pause(); 56 else if (k < 32) 57 return Thread.yield(); 58 Thread.sleep(1.msecs); 59 } 60 61 private: 62 version (D_InlineAsm_X86) 63 enum X86 = true; 64 else version (D_InlineAsm_X86_64) 65 enum X86 = true; 66 else 67 enum X86 = false; 68 69 static if (X86) 70 enum pauseThresh = 16; 71 else 72 enum pauseThresh = 4; 73 74 size_t val; 75 Contention contention; 76 } 77 78 // aligned to cacheline to avoid false sharing 79 shared align(64) struct AlignedSpinLock 80 { 81 this(SpinLock.Contention contention) @trusted @nogc nothrow 82 { 83 impl = shared(SpinLock)(contention); 84 } 85 86 SpinLock impl; 87 alias impl this; 88 } 89