xref: /netbsd-src/external/gpl3/gcc/dist/libphobos/libdruntime/core/internal/spinlock.d (revision 0a3071956a3a9fdebdbf7f338cf2d439b45fc728)
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