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