CppCommon  1.0.4.1
C++ Common Library
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 
24 namespace CppCommon {
25 
27 
28 class NamedCriticalSection::Impl
29 {
30 public:
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 
159 private:
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 
195 const std::string& NamedCriticalSection::name() const { return impl().name(); }
196 
197 bool 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
215  Thread::Yield();
216  }
217 
218  // Failed to acquire named critical section
219  return false;
220  }
221 }
222 
223 void NamedCriticalSection::Lock() { impl().Lock(); }
224 void 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 uint64_t CurrentThreadId() noexcept
Get the current thread Id.
Definition: thread.cpp:60
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.
Definition: token_bucket.h:15
Shared memory type definition.
Thread definition.
Aligned storage validator definition.