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 */
32return not fLock_.test_and_set (memory_order_acquire);
33 }
34inlinevoid SpinLock::lock ()
35 {
36// Acquire lock. If / when fails, yield processor to avoid too much busy waiting.
37while (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 }
41inlinevoid SpinLock::unlock ()
42 {
43// Release on the flag itself - this is the store the next locker's acquire reads. @see try_lock ()