CellModules
mingw.condition_variable.h
Go to the documentation of this file.
1
21#ifndef MINGW_CONDITIONAL_VARIABLE_H
22#define MINGW_CONDITIONAL_VARIABLE_H
23
24#if !defined(__cplusplus) || (__cplusplus < 201103L)
25#error A C++11 compiler is required!
26#endif
27// Use the standard classes for std::, if available.
28#include <condition_variable>
29
30#include <atomic>
31#include <assert.h>
32#include <chrono>
33#include <system_error>
34#include <windows.h>
35#include "mingw.mutex.h"
36#include "mingw.shared_mutex.h"
37
39{
40#if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS)
41enum class cv_status { no_timeout, timeout };
42#else
43using std::cv_status;
44#endif
45namespace xp
46{
48{
49protected:
51 std::atomic<int> mNumWaiters;
52 HANDLE mSemaphore;
53 HANDLE mWakeEvent;
54public:
55 typedef HANDLE native_handle_type;
57 {
58 return mSemaphore;
59 }
63 :mMutex(), mNumWaiters(0),
64 mSemaphore(CreateSemaphore(NULL, 0, 0xFFFF, NULL)),
65 mWakeEvent(CreateEvent(NULL, FALSE, FALSE, NULL))
66 {}
68 {
69 CloseHandle(mWakeEvent);
70 CloseHandle(mSemaphore);
71 }
72protected:
73 template <class M>
74 bool wait_impl(M& lock, DWORD timeout)
75 {
76 {
77 lock_guard<recursive_mutex> guard(mMutex);
79 }
80 lock.unlock();
81 DWORD ret = WaitForSingleObject(mSemaphore, timeout);
82
84 SetEvent(mWakeEvent);
85 lock.lock();
86 if (ret == WAIT_OBJECT_0)
87 return true;
88 else if (ret == WAIT_TIMEOUT)
89 return false;
90//2 possible cases:
91//1)The point in notify_all() where we determine the count to
92//increment the semaphore with has not been reached yet:
93//we just need to decrement mNumWaiters, but setting the event does not hurt
94//
95//2)Semaphore has just been released with mNumWaiters just before
96//we decremented it. This means that the semaphore count
97//after all waiters finish won't be 0 - because not all waiters
98//woke up by acquiring the semaphore - we woke up by a timeout.
99//The notify_all() must handle this grafecully
100//
101 else
102 throw std::system_error(EPROTO, std::generic_category());
103 }
104public:
105 template <class M>
106 void wait(M& lock)
107 {
108 wait_impl(lock, INFINITE);
109 }
110 template <class M, class Predicate>
111 void wait(M& lock, Predicate pred)
112 {
113 while(!pred())
114 {
115 wait(lock);
116 };
117 }
118
119 void notify_all() noexcept
120 {
121 lock_guard<recursive_mutex> lock(mMutex); //block any further wait requests until all current waiters are unblocked
122 if (mNumWaiters.load() <= 0)
123 return;
124
125 ReleaseSemaphore(mSemaphore, mNumWaiters, NULL);
126 while(mNumWaiters > 0)
127 {
128 auto ret = WaitForSingleObject(mWakeEvent, 1000);
129 if (ret == WAIT_FAILED || ret == WAIT_ABANDONED)
130 std::terminate();
131 }
132 assert(mNumWaiters == 0);
133//in case some of the waiters timed out just after we released the
134//semaphore by mNumWaiters, it won't be zero now, because not all waiters
135//woke up by acquiring the semaphore. So we must zero the semaphore before
136//we accept waiters for the next event
137//See _wait_impl for details
138 while(WaitForSingleObject(mSemaphore, 0) == WAIT_OBJECT_0);
139 }
140 void notify_one() noexcept
141 {
142 lock_guard<recursive_mutex> lock(mMutex);
143 int targetWaiters = mNumWaiters.load() - 1;
144 if (targetWaiters <= -1)
145 return;
146 ReleaseSemaphore(mSemaphore, 1, NULL);
147 while(mNumWaiters > targetWaiters)
148 {
149 auto ret = WaitForSingleObject(mWakeEvent, 1000);
150 if (ret == WAIT_FAILED || ret == WAIT_ABANDONED)
151 std::terminate();
152 }
153 assert(mNumWaiters == targetWaiters);
154 }
155 template <class M, class Rep, class Period>
156 cv_status wait_for(M& lock,
157 const std::chrono::duration<Rep, Period>& rel_time)
158 {
159 using namespace std::chrono;
160 long long timeout = duration_cast<milliseconds>(rel_time).count();
161 if (timeout < 0)
162 timeout = 0;
163 bool ret = wait_impl(lock, (DWORD)timeout);
164 return ret?cv_status::no_timeout:cv_status::timeout;
165 }
166
167 template <class M, class Rep, class Period, class Predicate>
168 bool wait_for(M& lock,
169 const std::chrono::duration<Rep, Period>& rel_time, Predicate pred)
170 {
171 return wait_until(lock, std::chrono::steady_clock::now()+rel_time, pred);
172 }
173 template <class M, class Clock, class Duration>
174 cv_status wait_until (M& lock,
175 const std::chrono::time_point<Clock,Duration>& abs_time)
176 {
177 return wait_for(lock, abs_time - Clock::now());
178 }
179 template <class M, class Clock, class Duration, class Predicate>
180 bool wait_until (M& lock,
181 const std::chrono::time_point<Clock, Duration>& abs_time,
182 Predicate pred)
183 {
184 while (!pred())
185 {
186 if (wait_until(lock, abs_time) == cv_status::timeout)
187 {
188 return pred();
189 }
190 }
191 return true;
192 }
193};
195{
196protected:
198public:
201 using base::base;
202 using base::notify_all;
203 using base::notify_one;
204 void wait(unique_lock<mutex> &lock)
205 {
206 base::wait(lock);
207 }
208 template <class Predicate>
209 void wait(unique_lock<mutex>& lock, Predicate pred)
210 {
211 base::wait(lock, pred);
212 }
213 template <class Rep, class Period>
214 cv_status wait_for(unique_lock<mutex>& lock, const std::chrono::duration<Rep, Period>& rel_time)
215 {
216 return base::wait_for(lock, rel_time);
217 }
218 template <class Rep, class Period, class Predicate>
219 bool wait_for(unique_lock<mutex>& lock, const std::chrono::duration<Rep, Period>& rel_time, Predicate pred)
220 {
221 return base::wait_for(lock, rel_time, pred);
222 }
223 template <class Clock, class Duration>
224 cv_status wait_until (unique_lock<mutex>& lock, const std::chrono::time_point<Clock,Duration>& abs_time)
225 {
226 return base::wait_until(lock, abs_time);
227 }
228 template <class Clock, class Duration, class Predicate>
229 bool wait_until (unique_lock<mutex>& lock, const std::chrono::time_point<Clock, Duration>& abs_time, Predicate pred)
230 {
231 return base::wait_until(lock, abs_time, pred);
232 }
233};
234} // Namespace mingw_stdthread::xp
235
236#if (WINVER >= _WIN32_WINNT_VISTA)
237namespace vista
238{
239// If compiling for Vista or higher, use the native condition variable.
241{
242protected:
243 CONDITION_VARIABLE cvariable_;
244
245#ifndef STDMUTEX_NO_RECURSION_CHECKS
246 template<typename MTX>
247 inline static void before_wait (MTX * pmutex)
248 {
249 pmutex->mOwnerThread.checkSetOwnerBeforeUnlock();
250 }
251 template<typename MTX>
252 inline static void after_wait (MTX * pmutex)
253 {
254 pmutex->mOwnerThread.setOwnerAfterLock(GetCurrentThreadId());
255 }
256#else
257 inline static void before_wait (void *) { }
258 inline static void after_wait (void *) { }
259#endif
260
261 bool wait_impl (unique_lock<xp::mutex> & lock, DWORD time)
262 {
263 static_assert(std::is_same<typename xp::mutex::native_handle_type, PCRITICAL_SECTION>::value,
264 "Native Win32 condition variable requires std::mutex to \
265use native Win32 critical section objects.");
266 xp::mutex * pmutex = lock.release();
267 before_wait(pmutex);
268 BOOL success = SleepConditionVariableCS(&cvariable_,
269 pmutex->native_handle(),
270 time);
271 after_wait(pmutex);
272 lock = unique_lock<xp::mutex>(*pmutex, adopt_lock);
273 return success;
274 }
275
276 bool wait_impl (unique_lock<windows7::mutex> & lock, DWORD time)
277 {
278 windows7::mutex * pmutex = lock.release();
279 before_wait(pmutex);
280 BOOL success = SleepConditionVariableSRW( native_handle(),
281 pmutex->native_handle(),
282 time, 0);
283 after_wait(pmutex);
284 lock = unique_lock<windows7::mutex>(*pmutex, adopt_lock);
285 return success;
286 }
287public:
288 typedef PCONDITION_VARIABLE native_handle_type;
290 {
291 return &cvariable_;
292 }
293
295 : cvariable_()
296 {
297 InitializeConditionVariable(&cvariable_);
298 }
299
300 ~condition_variable (void) = default;
301
304
305 void notify_one (void) noexcept
306 {
307 WakeConditionVariable(&cvariable_);
308 }
309
310 void notify_all (void) noexcept
311 {
312 WakeAllConditionVariable(&cvariable_);
313 }
314
315 void wait (unique_lock<mutex> & lock)
316 {
317 wait_impl(lock, INFINITE);
318 }
319
320 template<class Predicate>
321 void wait (unique_lock<mutex> & lock, Predicate pred)
322 {
323 while (!pred())
324 wait(lock);
325 }
326
327 template <class Rep, class Period>
328 cv_status wait_for(unique_lock<mutex>& lock,
329 const std::chrono::duration<Rep, Period>& rel_time)
330 {
331 using namespace std::chrono;
332 auto time = duration_cast<milliseconds>(rel_time).count();
333 if (time < 0)
334 time = 0;
335 bool result = wait_impl(lock, static_cast<DWORD>(time));
336 return result ? cv_status::no_timeout : cv_status::timeout;
337 }
338
339 template <class Rep, class Period, class Predicate>
340 bool wait_for(unique_lock<mutex>& lock,
341 const std::chrono::duration<Rep, Period>& rel_time,
342 Predicate pred)
343 {
344 return wait_until(lock,
345 std::chrono::steady_clock::now() + rel_time,
346 std::move(pred));
347 }
348 template <class Clock, class Duration>
349 cv_status wait_until (unique_lock<mutex>& lock,
350 const std::chrono::time_point<Clock,Duration>& abs_time)
351 {
352 return wait_for(lock, abs_time - Clock::now());
353 }
354 template <class Clock, class Duration, class Predicate>
355 bool wait_until (unique_lock<mutex>& lock,
356 const std::chrono::time_point<Clock, Duration>& abs_time,
357 Predicate pred)
358 {
359 while (!pred())
360 {
361 if (wait_until(lock, abs_time) == cv_status::timeout)
362 {
363 return pred();
364 }
365 }
366 return true;
367 }
368};
369
371{
372protected:
374 typedef windows7::shared_mutex native_shared_mutex;
375
377
378 template<class L>
379 bool wait_impl (L & lock, DWORD time)
380 {
381 unique_lock<mutex> internal_lock(internal_mutex_);
382 lock.unlock();
383 bool success = base::wait_impl(internal_lock, time);
384 lock.lock();
385 return success;
386 }
387// If the lock happens to be called on a native Windows mutex, skip any extra
388// contention.
389 inline bool wait_impl (unique_lock<mutex> & lock, DWORD time)
390 {
391 return base::wait_impl(lock, time);
392 }
393// Some shared_mutex functionality is available even in Vista, but it's not
394// until Windows 7 that a full implementation is natively possible. The class
395// itself is defined, with missing features, at the Vista feature level.
396 static_assert(CONDITION_VARIABLE_LOCKMODE_SHARED != 0, "The flag \
397CONDITION_VARIABLE_LOCKMODE_SHARED is not defined as expected. The flag for \
398exclusive mode is unknown (not specified by Microsoft Dev Center), but assumed \
399to be 0. There is a conflict with CONDITION_VARIABLE_LOCKMODE_SHARED.");
400//#if (WINVER >= _WIN32_WINNT_VISTA)
401 bool wait_impl (unique_lock<native_shared_mutex> & lock, DWORD time)
402 {
403 native_shared_mutex * pmutex = lock.release();
404 BOOL success = SleepConditionVariableSRW( base::native_handle(),
405 pmutex->native_handle(), time, 0);
406 lock = unique_lock<native_shared_mutex>(*pmutex, adopt_lock);
407 return success;
408 }
410 {
411 native_shared_mutex * pmutex = lock.release();
412 BOOL success = SleepConditionVariableSRW( base::native_handle(),
413 pmutex->native_handle(), time,
414 CONDITION_VARIABLE_LOCKMODE_SHARED);
415 lock = shared_lock<native_shared_mutex>(*pmutex, adopt_lock);
416 return success;
417 }
418//#endif
419public:
422
424 : base(), internal_mutex_()
425 {
426 }
427
428 ~condition_variable_any (void) = default;
429
430 using base::notify_one;
431 using base::notify_all;
432
433 template<class L>
434 void wait (L & lock)
435 {
436 wait_impl(lock, INFINITE);
437 }
438
439 template<class L, class Predicate>
440 void wait (L & lock, Predicate pred)
441 {
442 while (!pred())
443 wait(lock);
444 }
445
446 template <class L, class Rep, class Period>
447 cv_status wait_for(L& lock, const std::chrono::duration<Rep, Period>& period)
448 {
449 using namespace std::chrono;
450 auto time = duration_cast<milliseconds>(period).count();
451 if (time < 0)
452 time = 0;
453 bool result = wait_impl(lock, static_cast<DWORD>(time));
454 return result ? cv_status::no_timeout : cv_status::timeout;
455 }
456
457 template <class L, class Rep, class Period, class Predicate>
458 bool wait_for(L& lock, const std::chrono::duration<Rep, Period>& period,
459 Predicate pred)
460 {
461 return wait_until(lock, std::chrono::steady_clock::now() + period,
462 std::move(pred));
463 }
464 template <class L, class Clock, class Duration>
465 cv_status wait_until (L& lock,
466 const std::chrono::time_point<Clock,Duration>& abs_time)
467 {
468 return wait_for(lock, abs_time - Clock::now());
469 }
470 template <class L, class Clock, class Duration, class Predicate>
471 bool wait_until (L& lock,
472 const std::chrono::time_point<Clock, Duration>& abs_time,
473 Predicate pred)
474 {
475 while (!pred())
476 {
477 if (wait_until(lock, abs_time) == cv_status::timeout)
478 {
479 return pred();
480 }
481 }
482 return true;
483 }
484};
485} // Namespace vista
486#endif
487#if WINVER < 0x0600
488using xp::condition_variable;
489using xp::condition_variable_any;
490#else
491using vista::condition_variable;
492using vista::condition_variable_any;
493#endif
494} // Namespace mingw_stdthread
495
496// Push objects into std, but only if they are not already there.
497namespace std
498{
499// Because of quirks of the compiler, the common "using namespace std;"
500// directive would flatten the namespaces and introduce ambiguity where there
501// was none. Direct specification (std::), however, would be unaffected.
502// Take the safe option, and include only in the presence of MinGW's win32
503// implementation.
504#if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS)
505using mingw_stdthread::cv_status;
508#elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING) // Skip repetition
509#define MINGW_STDTHREAD_REDUNDANCY_WARNING
510#pragma message "This version of MinGW seems to include a win32 port of\
511 pthreads, and probably already has C++11 std threading classes implemented,\
512 based on pthreads. These classes, found in namespace std, are not overridden\
513 by the mingw-std-thread library. If you would still like to use this\
514 implementation (as it is more lightweight), use the classes provided in\
515 namespace mingw_stdthread."
516#endif
517}
518#endif // MINGW_CONDITIONAL_VARIABLE_H
mutex_type * release(void) noexcept
cv_status wait_until(L &lock, const std::chrono::time_point< Clock, Duration > &abs_time)
bool wait_until(L &lock, const std::chrono::time_point< Clock, Duration > &abs_time, Predicate pred)
bool wait_for(L &lock, const std::chrono::duration< Rep, Period > &period, Predicate pred)
bool wait_impl(unique_lock< mutex > &lock, DWORD time)
bool wait_impl(unique_lock< native_shared_mutex > &lock, DWORD time)
cv_status wait_for(L &lock, const std::chrono::duration< Rep, Period > &period)
bool wait_impl(shared_lock< native_shared_mutex > &lock, DWORD time)
bool wait_impl(unique_lock< windows7::mutex > &lock, DWORD time)
void wait(unique_lock< mutex > &lock, Predicate pred)
condition_variable & operator=(const condition_variable &)=delete
cv_status wait_until(unique_lock< mutex > &lock, const std::chrono::time_point< Clock, Duration > &abs_time)
bool wait_for(unique_lock< mutex > &lock, const std::chrono::duration< Rep, Period > &rel_time, Predicate pred)
bool wait_impl(unique_lock< xp::mutex > &lock, DWORD time)
cv_status wait_for(unique_lock< mutex > &lock, const std::chrono::duration< Rep, Period > &rel_time)
condition_variable(const condition_variable &)=delete
bool wait_until(unique_lock< mutex > &lock, const std::chrono::time_point< Clock, Duration > &abs_time, Predicate pred)
bool wait_for(M &lock, const std::chrono::duration< Rep, Period > &rel_time, Predicate pred)
condition_variable_any & operator=(const condition_variable_any &)=delete
cv_status wait_until(M &lock, const std::chrono::time_point< Clock, Duration > &abs_time)
bool wait_until(M &lock, const std::chrono::time_point< Clock, Duration > &abs_time, Predicate pred)
cv_status wait_for(M &lock, const std::chrono::duration< Rep, Period > &rel_time)
condition_variable_any(const condition_variable_any &)=delete
bool wait_until(unique_lock< mutex > &lock, const std::chrono::time_point< Clock, Duration > &abs_time, Predicate pred)
cv_status wait_until(unique_lock< mutex > &lock, const std::chrono::time_point< Clock, Duration > &abs_time)
void wait(unique_lock< mutex > &lock, Predicate pred)
cv_status wait_for(unique_lock< mutex > &lock, const std::chrono::duration< Rep, Period > &rel_time)
bool wait_for(unique_lock< mutex > &lock, const std::chrono::duration< Rep, Period > &rel_time, Predicate pred)
native_handle_type native_handle(void)
Definition: mingw.mutex.h:319
std::mutex et al implementation for MinGW (c) 2013-2016 by Mega Limited, Auckland,...
#define EPROTO
Definition: mingw.mutex.h:47
Standard-compliant shared_mutex for MinGW.
Provides common mathematical functions and vector operations.
Definition: std.hpp:4