| /** | |
| * SpinLock for runtime internal usage. | |
| * | |
| * Copyright: Copyright Digital Mars 2015 -. | |
| * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). | |
| * Authors: Martin Nowak | |
| * Source: $(DRUNTIMESRC core/internal/_spinlock.d) | |
| */ | |
| module core.internal.spinlock; | |
| import core.atomic, core.thread; | |
| shared struct SpinLock | |
| { | |
| /// for how long is the lock usually contended | |
| enum Contention : ubyte | |
| { | |
| brief, | |
| medium, | |
| lengthy, | |
| } | |
| @trusted @nogc nothrow: | |
| this(Contention contention) | |
| { | |
| this.contention = contention; | |
| } | |
| void lock() | |
| { | |
| if (cas(&val, size_t(0), size_t(1))) | |
| return; | |
| // Try to reduce the chance of another cas failure | |
| // TTAS lock (https://en.wikipedia.org/wiki/Test_and_test-and-set) | |
| immutable step = 1 << contention; | |
| while (true) | |
| { | |
| for (size_t n; atomicLoad!(MemoryOrder.raw)(val); n += step) | |
| yield(n); | |
| if (cas(&val, size_t(0), size_t(1))) | |
| return; | |
| } | |
| } | |
| void unlock() | |
| { | |
| atomicStore!(MemoryOrder.rel)(val, size_t(0)); | |
| } | |
| /// yield with backoff | |
| void yield(size_t k) | |
| { | |
| import core.time; | |
| if (k < pauseThresh) | |
| return core.atomic.pause(); | |
| else // if (k < 32) | |
| return Thread.yield(); | |
| // Thread.sleep(1.msecs); | |
| } | |
| private: | |
| version (D_InlineAsm_X86) | |
| enum X86 = true; | |
| else version (D_InlineAsm_X86_64) | |
| enum X86 = true; | |
| else | |
| enum X86 = false; | |
| static if (X86) | |
| enum pauseThresh = 16; | |
| else | |
| enum pauseThresh = 4; | |
| size_t val; | |
| Contention contention; | |
| } | |
| // aligned to cacheline to avoid false sharing | |
| shared align(64) struct AlignedSpinLock | |
| { | |
| this(SpinLock.Contention contention) @trusted @nogc nothrow | |
| { | |
| impl = shared(SpinLock)(contention); | |
| } | |
| SpinLock impl; | |
| alias impl this; | |
| } |