1 /** 2 * SpinLock for runtime internal usage. 3 * 4 * Copyright: Copyright Digital Mars 2015 -. 5 * License: $(WEB 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 if (k < pauseThresh) 54 return pause(); 55 else if (k < 32) 56 return Thread.yield(); 57 Thread.sleep(1.msecs); 58 } 59 60 private: 61 version (D_InlineAsm_X86) 62 enum X86 = true; 63 else version (D_InlineAsm_X86_64) 64 enum X86 = true; 65 else 66 enum X86 = false; 67 68 static if (X86) 69 { 70 enum pauseThresh = 16; pauseSpinLock71 void pause() 72 { 73 asm @trusted @nogc nothrow 74 { 75 // pause instruction 76 rep; 77 nop; 78 } 79 } 80 } 81 else 82 { 83 enum pauseThresh = 4; pauseSpinLock84 void pause() 85 { 86 } 87 } 88 89 size_t val; 90 Contention contention; 91 } 92 93 // aligned to cacheline to avoid false sharing 94 shared align(64) struct AlignedSpinLock 95 { 96 this(SpinLock.Contention contention) 97 { 98 impl = shared(SpinLock)(contention); 99 } 100 101 SpinLock impl; 102 alias impl this; 103 } 104