1151937Sjkim/**
2151937Sjkim * SpinLock for runtime internal usage.
3151937Sjkim *
4167802Sjkim * Copyright: Copyright Digital Mars 2015 -.
5151937Sjkim * License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
6151937Sjkim * Authors:   Martin Nowak
7151937Sjkim * Source: $(DRUNTIMESRC core/internal/_spinlock.d)
8151937Sjkim */
9151937Sjkimmodule core.internal.spinlock;
10151937Sjkim
11151937Sjkimimport core.atomic, core.thread;
12167802Sjkim
13151937Sjkimshared struct SpinLock
14151937Sjkim{
15151937Sjkim    /// for how long is the lock usually contended
16151937Sjkim    enum Contention : ubyte
17151937Sjkim    {
18151937Sjkim        brief,
19151937Sjkim        medium,
20151937Sjkim        lengthy,
21151937Sjkim    }
22151937Sjkim
23151937Sjkim@trusted @nogc nothrow:
24151937Sjkim    this(Contention contention)
25151937Sjkim    {
26151937Sjkim        this.contention = contention;
27151937Sjkim    }
28151937Sjkim
29151937Sjkim    void lock()
30151937Sjkim    {
31151937Sjkim        if (cas(&val, size_t(0), size_t(1)))
32151937Sjkim            return;
33151937Sjkim        // Try to reduce the chance of another cas failure
34151937Sjkim        // TTAS lock (https://en.wikipedia.org/wiki/Test_and_test-and-set)
35151937Sjkim        immutable step = 1 << contention;
36151937Sjkim        while (true)
37151937Sjkim        {
38151937Sjkim            for (size_t n; atomicLoad!(MemoryOrder.raw)(val); n += step)
39151937Sjkim                yield(n);
40151937Sjkim            if (cas(&val, size_t(0), size_t(1)))
41151937Sjkim                return;
42151937Sjkim        }
43151937Sjkim    }
44151937Sjkim
45151937Sjkim    void unlock()
46151937Sjkim    {
47151937Sjkim        atomicStore!(MemoryOrder.rel)(val, size_t(0));
48151937Sjkim    }
49151937Sjkim
50151937Sjkim    /// yield with backoff
51151937Sjkim    void yield(size_t k)
52151937Sjkim    {
53151937Sjkim        import core.time;
54151937Sjkim        if (k < pauseThresh)
55151937Sjkim            return core.atomic.pause();
56151937Sjkim        else if (k < 32)
57151937Sjkim            return Thread.yield();
58151937Sjkim        Thread.sleep(1.msecs);
59151937Sjkim    }
60151937Sjkim
61151937Sjkimprivate:
62151937Sjkim    version (D_InlineAsm_X86)
63151937Sjkim        enum X86 = true;
64151937Sjkim    else version (D_InlineAsm_X86_64)
65151937Sjkim        enum X86 = true;
66151937Sjkim    else
67151937Sjkim        enum X86 = false;
68151937Sjkim
69151937Sjkim    static if (X86)
70151937Sjkim        enum pauseThresh = 16;
71151937Sjkim    else
72151937Sjkim        enum pauseThresh = 4;
73151937Sjkim
74151937Sjkim    size_t val;
75151937Sjkim    Contention contention;
76151937Sjkim}
77151937Sjkim
78151937Sjkim// aligned to cacheline to avoid false sharing
79151937Sjkimshared align(64) struct AlignedSpinLock
80151937Sjkim{
81151937Sjkim    this(SpinLock.Contention contention) @trusted @nogc nothrow
82151937Sjkim    {
83151937Sjkim        impl = shared(SpinLock)(contention);
84151937Sjkim    }
85151937Sjkim
86151937Sjkim    SpinLock impl;
87151937Sjkim    alias impl this;
88151937Sjkim}
89151937Sjkim