CppCommon  1.0.4.1
C++ Common Library
named_condition_variable.cpp
Go to the documentation of this file.
1 
10 
11 #include "errors/fatal.h"
13 
14 #include <algorithm>
15 
16 #if (defined(unix) || defined(__unix) || defined(__unix__)) && !defined(__APPLE__) && !defined(__CYGWIN__)
17 #include "system/shared_type.h"
18 #include <pthread.h>
19 #elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
20 #include "system/shared_type.h"
21 #include <windows.h>
22 #undef max
23 #undef min
24 #endif
25 
26 namespace CppCommon {
27 
29 
30 class NamedConditionVariable::Impl
31 {
32 public:
33  Impl(const std::string& name) : _name(name)
34 #if (defined(unix) || defined(__unix) || defined(__unix__)) && !defined(__APPLE__) && !defined(__CYGWIN__)
35  , _shared(name)
36 #elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
37  , _shared(name)
38 #endif
39  {
40 #if defined(__APPLE__)
41  throwex SystemException("Named condition variable is not supported!");
42 #elif (defined(unix) || defined(__unix) || defined(__unix__)) && !defined(__CYGWIN__)
43  // Only the owner should initializate a named condition variable
44  if (_shared.owner())
45  {
46  pthread_mutexattr_t mutex_attribute;
47  int result = pthread_mutexattr_init(&mutex_attribute);
48  if (result != 0)
49  throwex SystemException("Failed to initialize a mutex attribute for the named condition variable!", result);
50  result = pthread_mutexattr_setpshared(&mutex_attribute, PTHREAD_PROCESS_SHARED);
51  if (result != 0)
52  throwex SystemException("Failed to set a mutex process shared attribute for the named condition variable!", result);
53  result = pthread_mutex_init(&_shared->mutex, &mutex_attribute);
54  if (result != 0)
55  throwex SystemException("Failed to initialize a mutex for the named condition variable!", result);
56  result = pthread_mutexattr_destroy(&mutex_attribute);
57  if (result != 0)
58  throwex SystemException("Failed to destroy a mutex attribute for the named condition variable!", result);
59 
60  pthread_condattr_t cond_attribute;
61  result = pthread_condattr_init(&cond_attribute);
62  if (result != 0)
63  throwex SystemException("Failed to initialize a conditional variable attribute for the named condition variable!", result);
64  result = pthread_condattr_setpshared(&cond_attribute, PTHREAD_PROCESS_SHARED);
65  if (result != 0)
66  throwex SystemException("Failed to set a conditional variable process shared attribute for the named condition variable!", result);
67  result = pthread_cond_init(&_shared->cond, &cond_attribute);
68  if (result != 0)
69  throwex SystemException("Failed to initialize a conditional variable for the named condition variable!", result);
70  result = pthread_condattr_destroy(&cond_attribute);
71  if (result != 0)
72  throwex SystemException("Failed to destroy a conditional variable attribute for the named condition variable!", result);
73  }
74 #elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
75  // Owner of the condition variable should initialize its value
76  if (_shared.owner())
77  *_shared = 0;
78  _mutex = CreateMutexA(nullptr, FALSE, (name + "-mutex").c_str());
79  if (_mutex == nullptr)
80  throwex SystemException("Failed to create or open a named mutex for the named condition variable!");
81  _semaphore = CreateSemaphoreA(nullptr, 0, std::numeric_limits<LONG>::max(), (name + "-semaphore").c_str());
82  if (_semaphore == nullptr)
83  throwex SystemException("Failed to create or open a named semaphore for the named condition variable!");
84 #endif
85  }
86 
87  ~Impl()
88  {
89 #if defined(__APPLE__)
90  fatality(SystemException("Named condition variable is not supported!"));
91 #elif (defined(unix) || defined(__unix) || defined(__unix__)) && !defined(__CYGWIN__)
92  // Only the owner should destroy a named condition variable
93  if (_shared.owner())
94  {
95  int result = pthread_mutex_destroy(&_shared->mutex);
96  if (result != 0)
97  fatality(SystemException("Failed to destroy a mutex for the named condition variable!", result));
98  result = pthread_cond_destroy(&_shared->cond);
99  if (result != 0)
100  fatality(SystemException("Failed to destroy a conditional variable for the named condition variable!", result));
101  }
102 #elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
103  if (!CloseHandle(_mutex))
104  fatality(SystemException("Failed to close a named mutex for the named condition variable!"));
105  if (!CloseHandle(_semaphore))
106  fatality(SystemException("Failed to close a named semaphore for the named condition variable!"));
107 #endif
108  }
109 
110  const std::string& name() const
111  {
112  return _name;
113  }
114 
115  void NotifyOne()
116  {
117 #if defined(__APPLE__)
118  throwex SystemException("Named condition variable is not supported!");
119 #elif (defined(unix) || defined(__unix) || defined(__unix__)) && !defined(__CYGWIN__)
120  int result = pthread_cond_signal(&_shared->cond);
121  if (result != 0)
122  throwex SystemException("Failed to signal a named condition variable!", result);
123 #elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
124  // Lock the named mutex
125  DWORD result = WaitForSingleObject(_mutex, INFINITE);
126  if (result != WAIT_OBJECT_0)
127  throwex SystemException("Failed to lock a named mutex for the named condition variable!");
128  // Decrement shared waiters count
129  --(*_shared);
130  // Signal one waiter
131  if (!ReleaseSemaphore(_semaphore, 1, nullptr))
132  throwex SystemException("Failed to release one semaphore waiter for the named condition variable!");
133  // Unlock the named mutex
134  if (!ReleaseMutex(_mutex))
135  throwex SystemException("Failed to unlock a named mutex for the named condition variable!");
136 #endif
137  }
138 
139  void NotifyAll()
140  {
141 #if defined(__APPLE__)
142  throwex SystemException("Named condition variable is not supported!");
143 #elif (defined(unix) || defined(__unix) || defined(__unix__)) && !defined(__CYGWIN__)
144  int result = pthread_cond_broadcast(&_shared->cond);
145  if (result != 0)
146  throwex SystemException("Failed to broadcast a named condition variable!", result);
147 #elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
148  // Lock the named mutex
149  DWORD result = WaitForSingleObject(_mutex, INFINITE);
150  if (result != WAIT_OBJECT_0)
151  throwex SystemException("Failed to lock a named mutex for the named condition variable!");
152  // Signal all waiters
153  if (!ReleaseSemaphore(_semaphore, *_shared, nullptr))
154  throwex SystemException("Failed to release all semaphore waiters for the named condition variable!");
155  // Clear all shared waiters
156  *_shared = 0;
157  // Unlock the named mutex
158  if (!ReleaseMutex(_mutex))
159  throwex SystemException("Failed to unlock a named mutex for the named condition variable!");
160 #endif
161  }
162 
163  void Wait()
164  {
165 #if defined(__APPLE__)
166  throwex SystemException("Named condition variable is not supported!");
167 #elif (defined(unix) || defined(__unix) || defined(__unix__)) && !defined(__CYGWIN__)
168  int result = pthread_cond_wait(&_shared->cond, &_shared->mutex);
169  if (result != 0)
170  throwex SystemException("Failed to waiting a named condition variable!", result);
171 #elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
172  // Lock the named mutex
173  DWORD result = WaitForSingleObject(_mutex, INFINITE);
174  if (result != WAIT_OBJECT_0)
175  throwex SystemException("Failed to lock a named mutex for the named condition variable!");
176  // Increment shared waiters count
177  ++(*_shared);
178  // Unlock the named mutex
179  if (!ReleaseMutex(_mutex))
180  throwex SystemException("Failed to unlock a named mutex for the named condition variable!");
181 
182  // Wait for the named condition variable
183  result = WaitForSingleObject(_semaphore, INFINITE);
184  if (result != WAIT_OBJECT_0)
185  throwex SystemException("Failed to wait a named condition variable!");
186 #endif
187  }
188 
189  bool TryWaitFor(const Timespan& timespan)
190  {
191  if (timespan < 0)
192  return false;
193 #if defined(__APPLE__)
194  throwex SystemException("Named condition variable is not supported!");
195 #elif (defined(unix) || defined(__unix) || defined(__unix__)) && !defined(__CYGWIN__)
196  struct timespec timeout;
197  timeout.tv_sec = timespan.seconds();
198  timeout.tv_nsec = timespan.nanoseconds() % 1000000000;
199  int result = pthread_cond_timedwait(&_shared->cond, &_shared->mutex, &timeout);
200  if ((result != 0) && (result != ETIMEDOUT))
201  throwex SystemException("Failed to waiting a named condition variable for the given timeout!", result);
202  return (result == 0);
203 #elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
204  // Lock the named mutex
205  DWORD result = WaitForSingleObject(_mutex, INFINITE);
206  if (result != WAIT_OBJECT_0)
207  throwex SystemException("Failed to lock a named mutex for the named condition variable!");
208  // Increment shared waiters count
209  ++(*_shared);
210  // Unlock the named mutex
211  if (!ReleaseMutex(_mutex))
212  throwex SystemException("Failed to unlock a named mutex for the named condition variable!");
213 
214  // Wait for the named condition variable
215  result = WaitForSingleObject(_semaphore, std::max((DWORD)0, (DWORD)timespan.milliseconds()));
216  if ((result != WAIT_OBJECT_0) && (result != WAIT_TIMEOUT))
217  throwex SystemException("Failed to try lock a named condition variable for the given timeout!");
218  return (result == WAIT_OBJECT_0);
219 #endif
220  }
221 
222 private:
223  std::string _name;
224 #if (defined(unix) || defined(__unix) || defined(__unix__)) && !defined(__APPLE__) && !defined(__CYGWIN__)
225  // Shared condition variable structure
226  struct CondVar
227  {
228  pthread_mutex_t mutex;
229  pthread_cond_t cond;
230  };
231 
232  // Shared condition variable structure wrapper
233  SharedType<CondVar> _shared;
234 #elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
235  HANDLE _mutex;
236  HANDLE _semaphore;
237  SharedType<LONG> _shared;
238 #endif
239 };
240 
242 
244 {
245  // Check implementation storage parameters
246  [[maybe_unused]] ValidateAlignedStorage<sizeof(Impl), alignof(Impl), StorageSize, StorageAlign> _;
247  static_assert((StorageSize >= sizeof(Impl)), "NamedConditionVariable::StorageSize must be increased!");
248  static_assert(((StorageAlign % alignof(Impl)) == 0), "NamedConditionVariable::StorageAlign must be adjusted!");
249 
250  // Create the implementation instance
251  new(&_storage)Impl(name);
252 }
253 
255 {
256  // Delete the implementation instance
257  reinterpret_cast<Impl*>(&_storage)->~Impl();
258 }
259 
260 const std::string& NamedConditionVariable::name() const { return impl().name(); }
261 
262 void NamedConditionVariable::NotifyOne() { impl().NotifyOne(); }
263 void NamedConditionVariable::NotifyAll() { impl().NotifyAll(); }
264 
265 void NamedConditionVariable::Wait() { impl().Wait(); }
266 
267 bool NamedConditionVariable::TryWaitFor(const Timespan& timespan) { return impl().TryWaitFor(timespan); }
268 
269 } // namespace CppCommon
void NotifyOne()
Notify one of waiting thread about event occurred.
void NotifyAll()
Notify all waiting threads about event occurred.
NamedConditionVariable(const std::string &name)
Default class constructor.
const std::string & name() const
Get the condition variable name.
void Wait()
Wait until condition variable is notified.
bool TryWaitFor(const Timespan &timespan)
Try to wait for the given timespan until condition variable is notified.
#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 condition variable synchronization primitive definition.
C++ Common project definitions.
Definition: token_bucket.h:15
Shared memory type definition.
Aligned storage validator definition.