CppCommon 1.0.5.0
C++ Common Library
Loading...
Searching...
No Matches
named_critical_section.cpp
Go to the documentation of this file.
1
10
11#include "errors/fatal.h"
12#include "system/shared_type.h"
14#include "threads/thread.h"
16
17#if (defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)) && !defined(__CYGWIN__)
18#include <pthread.h>
19#elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
20#include <windows.h>
21#undef Yield
22#endif
23
24namespace CppCommon {
25
27
28class NamedCriticalSection::Impl
29{
30public:
31 Impl(const std::string& name, uint32_t spin) : _shared(name), _event(name + "_event"), _spin(spin) {}
32
33 ~Impl()
34 {
35 if (_shared->thread_id == Thread::CurrentThreadId())
36 fatality(SystemException("Named critical section should not be destroyed if our thread owns it!"));
37 }
38
39 const std::string& name() const
40 {
41 return _shared.name();
42 }
43
44 bool TryLock()
45 {
46 return TryLock(0);
47 }
48
49 bool TryLock(uint32_t spin)
50 {
51 uint32_t iteration = 0;
52 uint64_t thread_id = Thread::CurrentThreadId();
53
54 while (true)
55 {
56 // If lock count = 0, the named critical section is unowned, we can own it
57#if (defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)) && !defined(__CYGWIN__)
58 if (__sync_bool_compare_and_swap(&_shared->lock_count, 0, 1))
59#elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
60 if (InterlockedCompareExchange(&_shared->lock_count, 1, 0) == 0)
61#endif
62 {
63 // The named critical section is unowned, let this thread own it once
64 _shared->thread_id = thread_id;
65 _shared->recurse_count = 1;
66 return true;
67 }
68 else if (_shared->thread_id == thread_id)
69 {
70 // If the named critical section is owned by this thread, own it again
71#if (defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)) && !defined(__CYGWIN__)
72 __sync_add_and_fetch(&_shared->lock_count, 1);
73#elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
74 InterlockedIncrement(&_shared->lock_count);
75#endif
76 _shared->recurse_count++;
77 return true;
78 }
79 else if (iteration++ >= spin)
80 break;
81
82 // Yield to other threads
83 Thread::Yield();
84 }
85
86 return false;
87 }
88
89 void Lock()
90 {
91 // Spin, trying to get the named critical section
92 if (TryLock(_spin))
93 return;
94
95 // We couldn't get the the named critical section, wait for it
96 uint64_t thread_id = Thread::CurrentThreadId();
97
98#if (defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)) && !defined(__CYGWIN__)
99 if (__sync_add_and_fetch(&_shared->lock_count, 1) == 1)
100#elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
101 if (InterlockedIncrement(&_shared->lock_count) == 1)
102#endif
103 {
104 // The named critical section is unowned, let this thread own it once
105 _shared->thread_id = thread_id;
106 _shared->recurse_count = 1;
107 }
108 else
109 {
110 if (_shared->thread_id == thread_id)
111 {
112 // If the named critical section is owned by this thread, own it again
113 _shared->recurse_count++;
114 }
115 else
116 {
117 // The named critical section is owned by another thread, wait for it
118 _event.Wait();
119
120 // The named critical section is unowned, let this thread own it once
121 _shared->thread_id = thread_id;
122 _shared->recurse_count = 1;
123 }
124 }
125 }
126
127 void Unlock()
128 {
129 if (_shared->thread_id != Thread::CurrentThreadId())
130 throwex SystemException("Named critical section can not be unlocked from other thread!");
131
132 // Reduce this thread's ownership of the named critical section
133 if (--_shared->recurse_count > 0)
134 {
135 // We still own the named critical section
136#if (defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)) && !defined(__CYGWIN__)
137 __sync_sub_and_fetch(&_shared->lock_count, 1);
138#elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
139 InterlockedDecrement(&_shared->lock_count);
140#endif
141 }
142 else
143 {
144 // We don't own the named critical section anymore
145 _shared->thread_id = 0;
146
147#if (defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)) && !defined(__CYGWIN__)
148 if (__sync_sub_and_fetch(&_shared->lock_count, 1) > 0)
149#elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
150 if (InterlockedDecrement(&_shared->lock_count) > 0)
151#endif
152 {
153 // Other threads are waiting, the auto-reset event wakes one of them
154 _event.Signal();
155 }
156 }
157 }
158
159private:
160 // Shared critical section structure
161 struct CriticalSectionHeader
162 {
163 volatile uint64_t lock_count;
164 int recurse_count;
165 uint64_t thread_id;
166
167 CriticalSectionHeader() : lock_count(0), recurse_count(0), thread_id(0) {}
168 };
169
170 // Shared critical section structure wrapper
171 SharedType<CriticalSectionHeader> _shared;
172 NamedEventAutoReset _event;
173 uint32_t _spin;
174};
175
177
179{
180 // Check implementation storage parameters
181 [[maybe_unused]] ValidateAlignedStorage<sizeof(Impl), alignof(Impl), StorageSize, StorageAlign> _;
182 static_assert((StorageSize >= sizeof(Impl)), "NamedCriticalSection::StorageSize must be increased!");
183 static_assert(((StorageAlign % alignof(Impl)) == 0), "NamedCriticalSection::StorageAlign must be adjusted!");
184
185 // Create the implementation instance
186 new(&_storage)Impl(name, 4000);
187}
188
190{
191 // Delete the implementation instance
192 reinterpret_cast<Impl*>(&_storage)->~Impl();
193}
194
195const std::string& NamedCriticalSection::name() const { return impl().name(); }
196
197bool NamedCriticalSection::TryLock() { return impl().TryLock(); }
198
200{
201 // Calculate a finish timestamp
202 Timestamp finish = NanoTimestamp() + timespan;
203
204 // Try to acquire named critical section at least one time
205 if (TryLock())
206 return true;
207 else
208 {
209 // Try lock or yield for the given timespan
210 while (NanoTimestamp() < finish)
211 {
212 if (TryLock())
213 return true;
214 else
216 }
217
218 // Failed to acquire named critical section
219 return false;
220 }
221}
222
223void NamedCriticalSection::Lock() { impl().Lock(); }
224void NamedCriticalSection::Unlock() { impl().Unlock(); }
225
226} // namespace CppCommon
bool TryLock()
Try to acquire critical section without block.
void Lock()
Acquire critical section with block.
void Unlock()
Release critical section.
const std::string & name() const
Get the critical section name.
NamedCriticalSection(const std::string &name)
Default class constructor.
bool TryLockFor(const Timespan &timespan)
Try to acquire critical section for the given timespan.
High resolution timestamp.
Definition timestamp.h:272
static void Yield() noexcept
Yield to other threads.
Definition thread.cpp:143
#define throwex
Throw extended exception macro.
Definition exceptions.h:23
Fatal abort execution definition.
#define fatality(...)
Fatal abort execution extended macro.
Definition fatal.h:22
Named critical section synchronization primitive definition.
Named auto-reset event synchronization primitive definition.
C++ Common project definitions.
Shared memory type definition.
Thread definition.
Aligned storage validator definition.