Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
SpinLock.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include <thread>
5
7
8 /*
9 ********************************************************************************
10 ******************************** Execution::SpinLock ***************************
11 ********************************************************************************
12 */
13 inline bool SpinLock::try_lock ()
14 {
15 /*
16 * Acquire on the flag itself is all this does, and all a lock needs.
17 *
18 * When test_and_set () returns false we took the lock, so this read-modify-write read the value written
19 * by the previous holder's unlock () - a release store. An acquire that reads from a release store
20 * synchronizes-with it, making everything the previous holder did before unlocking happen-before
21 * everything this thread does after locking. That is exactly what the data under the lock needs, and a
22 * separate fence would add nothing to it.
23 * @see https://github.com/SophistSolutions/Stroika/issues/628 (STK-494), where that was settled.
24 *
25 * \note A FAILED try_lock () acquires nothing, so it needs no ordering at all - but atomic_flag has no
26 * separate failure order (unlike compare_exchange_xxx), so the acquire is paid either way. Free
27 * on x86-64, not on weakly ordered hardware. Avoiding it means spinning on a relaxed test () and
28 * only attempting this RMW once the flag reads clear, which would also stop the spin dirtying
29 * the cache line every iteration. Not done here: it changes the contended path, so it wants its
30 * own measurement.
31 */
32 return not fLock_.test_and_set (memory_order_acquire);
33 }
34 inline void SpinLock::lock ()
35 {
36 // Acquire lock. If / when fails, yield processor to avoid too much busy waiting.
37 while (not try_lock ()) {
38 this_thread::yield (); // nb: until Stroika v2.0a209, this called Execution::Yield (), making this a cancelation point. That is principally bad because it makes SpinLock not interchangeable with mutex
39 }
40 }
41 inline void SpinLock::unlock ()
42 {
43 // Release on the flag itself - this is the store the next locker's acquire reads. @see try_lock ()
44 fLock_.clear (memory_order_release);
45 }
46
47}