Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
SpinLock.h
Go to the documentation of this file.
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Execution_SpinLock_h_
5#define _Stroika_Foundation_Execution_SpinLock_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <atomic>
10
11#include "Stroika/Foundation/Common/Common.h"
14
15/**
16 * \file
17 */
18
20
21 /**
22 * SpinLock and mutex can be nearly used interchangeably. Oftentimes, users will want to define a typedef which selects
23 * the faster implementation.
24 *
25 * \note Stroika 2.0a155 and earlier - Execution::kSpinLock_IsFasterThan_mutex was always true
26 *
27 * \note Stroika 2.0a156 and later - due to threadFence and http://stroika-bugs.sophists.com/browse/STK-494 - SpinLock
28 * slowed slightly, but its still notably faster (with the default barrier style) on gcc/unix/windows (x86 only tested).
29 */
30 constexpr bool kSpinLock_IsFasterThan_mutex = true;
31
32 /**
33 * Implementation based on
34 * http://en.cppreference.com/w/cpp/atomic/atomic_flag
35 *
36 * About to run tests to compare performance numbers. But this maybe useful for at least some(many) cases.
37 *
38 * \note - SpinLock - though generally faster than most mutexes for short accesses, are not recursive mutexes!
39 *
40 * \note ***Not Cancelation Point***
41 * SpinLock contains no cancelation points, and so can be used interchangeably with mutex - just for the performance differences
42 *
43 * \note From http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3690.pdf
44 * 1.10 Multi-threaded executions and data races [intro.multithread]
45 *
46 * "A synchronization operation without an associated memory location is a fence and
47 * can be either an acquire fence, a release fence, or both an acquire and release fence"
48 *
49 * Since a spinlock once acquired - can be used to assume assocated data (the data protected by the spinlock mutex)
50 * is up to date with respect to other threads and acquire is needed on the lock. And to assure any changes made with
51 * the lock are seen in other threads a release atomic_fence() is required on the unlock.
52 *
53 * This is the DEFAULT behavior we provide with the default BarrierType - eReleaseAcquire
54 *
55 * \note More good references (to verify if this logic is right - which I'm not 100% clear on)
56 * o https://www.boost.org/doc/libs/1_54_0/doc/html/atomic/usage_examples.html#boost_atomic.usage_examples.example_spinlock
57 * o https://blog.regehr.org/archives/2173
58 * This strongly suggests I may want to do a more fair spinlock, but doesn't appear to have
59 * the fences. In fact, he alleges if you do procedure properly you dont need the fence.
60 * HMM. Need good test cases before I can really play around with this very safely.
61 */
62 class SpinLock {
63 public:
64 /**
65 * @see std::atomic_thread_fence ()
66 * @see std::memory_order
67 *
68 * \note subtly - eReleaseAcquire means acquire on lock, and release on unlock. This is the natural default for a mutex.
69 */
70 enum class BarrierType {
71 eNoBarrier,
72 eReleaseAcquire,
73 eMemoryTotalOrder,
74
75 eDEFAULT = eReleaseAcquire,
76
77 Stroika_Define_Enum_Bounds (eNoBarrier, eMemoryTotalOrder)
78 };
79
80 public:
81 /**
82 * In typical usage, one would use a SpinLock as a mutex, and expect it to create a memory fence.
83 * However, sometimes you want to spinlock and handle the memory ordering yourself. So that feature
84 * is optional (defaulting to the safer, but slower - true).
85 */
86 SpinLock (BarrierType barrier = BarrierType::eDEFAULT);
87 SpinLock (const SpinLock&) = delete;
88
89 public:
90 ~SpinLock () = default;
91
92 public:
93 nonvirtual SpinLock& operator= (const SpinLock&) = delete;
94
95 public:
96 /**
97 */
98 nonvirtual bool try_lock ();
99
100 public:
101 /**
102 */
103 nonvirtual void lock ();
104
105 public:
106 /**
107 */
108 nonvirtual void unlock ();
109
110 private:
111 BarrierType fBarrierFlag_;
112
113 private:
114#if (__cplusplus < kStrokia_Foundation_Common_cplusplus_20)
115 atomic_flag fLock_ = ATOMIC_FLAG_INIT;
116#else
117 atomic_flag fLock_;
118#endif
119 };
120
121}
122
123/*
124 ********************************************************************************
125 ***************************** Implementation Details ***************************
126 ********************************************************************************
127 */
128#include "SpinLock.inl"
129
130#endif /*_Stroika_Foundation_Execution_SpinLock_h_*/
#define Stroika_Define_Enum_Bounds(FIRST_ITEM, LAST_ITEM)
constexpr bool kSpinLock_IsFasterThan_mutex
Definition SpinLock.h:30