src/third-party/discord-rpc/include/mingw-std-threads/condition_variable (view raw)
1/**
2* @file condition_variable.h
3* @brief std::condition_variable implementation for MinGW
4*
5* (c) 2013-2016 by Mega Limited, Auckland, New Zealand
6* @author Alexander Vassilev
7*
8* @copyright Simplified (2-clause) BSD License.
9* You should have received a copy of the license along with this
10* program.
11*
12* This code is distributed in the hope that it will be useful,
13* but WITHOUT ANY WARRANTY; without even the implied warranty of
14* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15* @note
16* This file may become part of the mingw-w64 runtime package. If/when this happens,
17* the appropriate license will be added, i.e. this code will become dual-licensed,
18* and the current BSD 2-clause license will stay.
19*/
20
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_next <condition_variable>
29
30#include <cassert>
31#include <chrono>
32#include <system_error>
33#include <windows.h>
34
35#if (WINVER < _WIN32_WINNT_VISTA)
36#include <atomic>
37#endif
38
39#include <mutex>
40#include <shared_mutex>
41
42#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0501)
43#error To use the MinGW-std-threads library, you will need to define the macro _WIN32_WINNT to be 0x0501 (Windows XP) or higher.
44#endif
45
46namespace mingw_stdthread
47{
48#if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS)
49enum class cv_status { no_timeout, timeout };
50#else
51using std::cv_status;
52#endif
53namespace xp
54{
55// Include the XP-compatible condition_variable classes only if actually
56// compiling for XP. The XP-compatible classes are slower than the newer
57// versions, and depend on features not compatible with Windows Phone 8.
58#if (WINVER < _WIN32_WINNT_VISTA)
59class condition_variable_any
60{
61 recursive_mutex mMutex {};
62 std::atomic<int> mNumWaiters {0};
63 HANDLE mSemaphore;
64 HANDLE mWakeEvent {};
65public:
66 using native_handle_type = HANDLE;
67 native_handle_type native_handle()
68 {
69 return mSemaphore;
70 }
71 condition_variable_any(const condition_variable_any&) = delete;
72 condition_variable_any& operator=(const condition_variable_any&) = delete;
73 condition_variable_any()
74 : mSemaphore(CreateSemaphore(NULL, 0, 0xFFFF, NULL))
75 {
76 if (mSemaphore == NULL)
77 throw std::system_error(GetLastError(), std::generic_category());
78 mWakeEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
79 if (mWakeEvent == NULL)
80 {
81 CloseHandle(mSemaphore);
82 throw std::system_error(GetLastError(), std::generic_category());
83 }
84 }
85 ~condition_variable_any()
86 {
87 CloseHandle(mWakeEvent);
88 CloseHandle(mSemaphore);
89 }
90private:
91 template <class M>
92 bool wait_impl(M& lock, DWORD timeout)
93 {
94 {
95 lock_guard<recursive_mutex> guard(mMutex);
96 mNumWaiters++;
97 }
98 lock.unlock();
99 DWORD ret = WaitForSingleObject(mSemaphore, timeout);
100
101 mNumWaiters--;
102 SetEvent(mWakeEvent);
103 lock.lock();
104 if (ret == WAIT_OBJECT_0)
105 return true;
106 else if (ret == WAIT_TIMEOUT)
107 return false;
108//2 possible cases:
109//1)The point in notify_all() where we determine the count to
110//increment the semaphore with has not been reached yet:
111//we just need to decrement mNumWaiters, but setting the event does not hurt
112//
113//2)Semaphore has just been released with mNumWaiters just before
114//we decremented it. This means that the semaphore count
115//after all waiters finish won't be 0 - because not all waiters
116//woke up by acquiring the semaphore - we woke up by a timeout.
117//The notify_all() must handle this gracefully
118//
119 else
120 {
121 using namespace std;
122 throw system_error(make_error_code(errc::protocol_error));
123 }
124 }
125public:
126 template <class M>
127 void wait(M& lock)
128 {
129 wait_impl(lock, INFINITE);
130 }
131 template <class M, class Predicate>
132 void wait(M& lock, Predicate pred)
133 {
134 while(!pred())
135 {
136 wait(lock);
137 };
138 }
139
140 void notify_all() noexcept
141 {
142 lock_guard<recursive_mutex> lock(mMutex); //block any further wait requests until all current waiters are unblocked
143 if (mNumWaiters.load() <= 0)
144 return;
145
146 ReleaseSemaphore(mSemaphore, mNumWaiters, NULL);
147 while(mNumWaiters > 0)
148 {
149 auto ret = WaitForSingleObject(mWakeEvent, 1000);
150 if (ret == WAIT_FAILED || ret == WAIT_ABANDONED)
151 std::terminate();
152 }
153 assert(mNumWaiters == 0);
154//in case some of the waiters timed out just after we released the
155//semaphore by mNumWaiters, it won't be zero now, because not all waiters
156//woke up by acquiring the semaphore. So we must zero the semaphore before
157//we accept waiters for the next event
158//See _wait_impl for details
159 while(WaitForSingleObject(mSemaphore, 0) == WAIT_OBJECT_0);
160 }
161 void notify_one() noexcept
162 {
163 lock_guard<recursive_mutex> lock(mMutex);
164 int targetWaiters = mNumWaiters.load() - 1;
165 if (targetWaiters <= -1)
166 return;
167 ReleaseSemaphore(mSemaphore, 1, NULL);
168 while(mNumWaiters > targetWaiters)
169 {
170 auto ret = WaitForSingleObject(mWakeEvent, 1000);
171 if (ret == WAIT_FAILED || ret == WAIT_ABANDONED)
172 std::terminate();
173 }
174 assert(mNumWaiters == targetWaiters);
175 }
176 template <class M, class Rep, class Period>
177 cv_status wait_for(M& lock,
178 const std::chrono::duration<Rep, Period>& rel_time)
179 {
180 using namespace std::chrono;
181 auto timeout = duration_cast<milliseconds>(rel_time).count();
182 DWORD waittime = (timeout < INFINITE) ? ((timeout < 0) ? 0 : static_cast<DWORD>(timeout)) : (INFINITE - 1);
183 bool ret = wait_impl(lock, waittime) || (timeout >= INFINITE);
184 return ret?cv_status::no_timeout:cv_status::timeout;
185 }
186
187 template <class M, class Rep, class Period, class Predicate>
188 bool wait_for(M& lock,
189 const std::chrono::duration<Rep, Period>& rel_time, Predicate pred)
190 {
191 return wait_until(lock, std::chrono::steady_clock::now()+rel_time, pred);
192 }
193 template <class M, class Clock, class Duration>
194 cv_status wait_until (M& lock,
195 const std::chrono::time_point<Clock,Duration>& abs_time)
196 {
197 return wait_for(lock, abs_time - Clock::now());
198 }
199 template <class M, class Clock, class Duration, class Predicate>
200 bool wait_until (M& lock,
201 const std::chrono::time_point<Clock, Duration>& abs_time,
202 Predicate pred)
203 {
204 while (!pred())
205 {
206 if (wait_until(lock, abs_time) == cv_status::timeout)
207 {
208 return pred();
209 }
210 }
211 return true;
212 }
213};
214class condition_variable: condition_variable_any
215{
216 using base = condition_variable_any;
217public:
218 using base::native_handle_type;
219 using base::native_handle;
220 using base::base;
221 using base::notify_all;
222 using base::notify_one;
223 void wait(unique_lock<mutex> &lock)
224 {
225 base::wait(lock);
226 }
227 template <class Predicate>
228 void wait(unique_lock<mutex>& lock, Predicate pred)
229 {
230 base::wait(lock, pred);
231 }
232 template <class Rep, class Period>
233 cv_status wait_for(unique_lock<mutex>& lock, const std::chrono::duration<Rep, Period>& rel_time)
234 {
235 return base::wait_for(lock, rel_time);
236 }
237 template <class Rep, class Period, class Predicate>
238 bool wait_for(unique_lock<mutex>& lock, const std::chrono::duration<Rep, Period>& rel_time, Predicate pred)
239 {
240 return base::wait_for(lock, rel_time, pred);
241 }
242 template <class Clock, class Duration>
243 cv_status wait_until (unique_lock<mutex>& lock, const std::chrono::time_point<Clock,Duration>& abs_time)
244 {
245 return base::wait_until(lock, abs_time);
246 }
247 template <class Clock, class Duration, class Predicate>
248 bool wait_until (unique_lock<mutex>& lock, const std::chrono::time_point<Clock, Duration>& abs_time, Predicate pred)
249 {
250 return base::wait_until(lock, abs_time, pred);
251 }
252};
253#endif // Compiling for XP
254} // Namespace mingw_stdthread::xp
255
256#if (WINVER >= _WIN32_WINNT_VISTA)
257namespace vista
258{
259// If compiling for Vista or higher, use the native condition variable.
260class condition_variable
261{
262#pragma GCC diagnostic push
263#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
264 CONDITION_VARIABLE cvariable_ = CONDITION_VARIABLE_INIT;
265#pragma GCC diagnostic pop
266
267 friend class condition_variable_any;
268
269#if STDMUTEX_RECURSION_CHECKS
270 template<typename MTX>
271 inline static void before_wait (MTX * pmutex)
272 {
273 pmutex->mOwnerThread.checkSetOwnerBeforeUnlock();
274 }
275 template<typename MTX>
276 inline static void after_wait (MTX * pmutex)
277 {
278 pmutex->mOwnerThread.setOwnerAfterLock(GetCurrentThreadId());
279 }
280#else
281 inline static void before_wait (void *) { }
282 inline static void after_wait (void *) { }
283#endif
284
285 bool wait_impl (unique_lock<xp::mutex> & lock, DWORD time)
286 {
287 using mutex_handle_type = typename xp::mutex::native_handle_type;
288 static_assert(std::is_same<mutex_handle_type, PCRITICAL_SECTION>::value,
289 "Native Win32 condition variable requires std::mutex to \
290use native Win32 critical section objects.");
291 xp::mutex * pmutex = lock.release();
292 before_wait(pmutex);
293 BOOL success = SleepConditionVariableCS(&cvariable_,
294 pmutex->native_handle(),
295 time);
296 after_wait(pmutex);
297 lock = unique_lock<xp::mutex>(*pmutex, adopt_lock);
298 return success;
299 }
300
301 bool wait_unique (windows7::mutex * pmutex, DWORD time)
302 {
303 before_wait(pmutex);
304 BOOL success = SleepConditionVariableSRW( native_handle(),
305 pmutex->native_handle(),
306 time,
307// CONDITION_VARIABLE_LOCKMODE_SHARED has a value not specified by
308// Microsoft's Dev Center, but is known to be (convertible to) a ULONG. To
309// ensure that the value passed to this function is not equal to Microsoft's
310// constant, we can either use a static_assert, or simply generate an
311// appropriate value.
312 !CONDITION_VARIABLE_LOCKMODE_SHARED);
313 after_wait(pmutex);
314 return success;
315 }
316 bool wait_impl (unique_lock<windows7::mutex> & lock, DWORD time)
317 {
318 windows7::mutex * pmutex = lock.release();
319 bool success = wait_unique(pmutex, time);
320 lock = unique_lock<windows7::mutex>(*pmutex, adopt_lock);
321 return success;
322 }
323public:
324 using native_handle_type = PCONDITION_VARIABLE;
325 native_handle_type native_handle (void)
326 {
327 return &cvariable_;
328 }
329
330 condition_variable (void) = default;
331 ~condition_variable (void) = default;
332
333 condition_variable (const condition_variable &) = delete;
334 condition_variable & operator= (const condition_variable &) = delete;
335
336 void notify_one (void) noexcept
337 {
338 WakeConditionVariable(&cvariable_);
339 }
340
341 void notify_all (void) noexcept
342 {
343 WakeAllConditionVariable(&cvariable_);
344 }
345
346 void wait (unique_lock<mutex> & lock)
347 {
348 wait_impl(lock, INFINITE);
349 }
350
351 template<class Predicate>
352 void wait (unique_lock<mutex> & lock, Predicate pred)
353 {
354 while (!pred())
355 wait(lock);
356 }
357
358 template <class Rep, class Period>
359 cv_status wait_for(unique_lock<mutex>& lock,
360 const std::chrono::duration<Rep, Period>& rel_time)
361 {
362 using namespace std::chrono;
363 auto timeout = duration_cast<milliseconds>(rel_time).count();
364 DWORD waittime = (timeout < INFINITE) ? ((timeout < 0) ? 0 : static_cast<DWORD>(timeout)) : (INFINITE - 1);
365 bool result = wait_impl(lock, waittime) || (timeout >= INFINITE);
366 return result ? cv_status::no_timeout : cv_status::timeout;
367 }
368
369 template <class Rep, class Period, class Predicate>
370 bool wait_for(unique_lock<mutex>& lock,
371 const std::chrono::duration<Rep, Period>& rel_time,
372 Predicate pred)
373 {
374 return wait_until(lock,
375 std::chrono::steady_clock::now() + rel_time,
376 std::move(pred));
377 }
378 template <class Clock, class Duration>
379 cv_status wait_until (unique_lock<mutex>& lock,
380 const std::chrono::time_point<Clock,Duration>& abs_time)
381 {
382 return wait_for(lock, abs_time - Clock::now());
383 }
384 template <class Clock, class Duration, class Predicate>
385 bool wait_until (unique_lock<mutex>& lock,
386 const std::chrono::time_point<Clock, Duration>& abs_time,
387 Predicate pred)
388 {
389 while (!pred())
390 {
391 if (wait_until(lock, abs_time) == cv_status::timeout)
392 {
393 return pred();
394 }
395 }
396 return true;
397 }
398};
399
400class condition_variable_any
401{
402 using native_shared_mutex = windows7::shared_mutex;
403
404 condition_variable internal_cv_ {};
405// When available, the SRW-based mutexes should be faster than the
406// CriticalSection-based mutexes. Only try_lock will be unavailable in Vista,
407// and try_lock is not used by condition_variable_any.
408 windows7::mutex internal_mutex_ {};
409
410 template<class L>
411 bool wait_impl (L & lock, DWORD time)
412 {
413 unique_lock<decltype(internal_mutex_)> internal_lock(internal_mutex_);
414 lock.unlock();
415 bool success = internal_cv_.wait_impl(internal_lock, time);
416 lock.lock();
417 return success;
418 }
419// If the lock happens to be called on a native Windows mutex, skip any extra
420// contention.
421 inline bool wait_impl (unique_lock<mutex> & lock, DWORD time)
422 {
423 return internal_cv_.wait_impl(lock, time);
424 }
425// Some shared_mutex functionality is available even in Vista, but it's not
426// until Windows 7 that a full implementation is natively possible. The class
427// itself is defined, with missing features, at the Vista feature level.
428 bool wait_impl (unique_lock<native_shared_mutex> & lock, DWORD time)
429 {
430 native_shared_mutex * pmutex = lock.release();
431 bool success = internal_cv_.wait_unique(pmutex, time);
432 lock = unique_lock<native_shared_mutex>(*pmutex, adopt_lock);
433 return success;
434 }
435 bool wait_impl (shared_lock<native_shared_mutex> & lock, DWORD time)
436 {
437 native_shared_mutex * pmutex = lock.release();
438 BOOL success = SleepConditionVariableSRW(native_handle(),
439 pmutex->native_handle(), time,
440 CONDITION_VARIABLE_LOCKMODE_SHARED);
441 lock = shared_lock<native_shared_mutex>(*pmutex, adopt_lock);
442 return success;
443 }
444public:
445 using native_handle_type = typename condition_variable::native_handle_type;
446
447 native_handle_type native_handle (void)
448 {
449 return internal_cv_.native_handle();
450 }
451
452 void notify_one (void) noexcept
453 {
454 internal_cv_.notify_one();
455 }
456
457 void notify_all (void) noexcept
458 {
459 internal_cv_.notify_all();
460 }
461
462 condition_variable_any (void) = default;
463 ~condition_variable_any (void) = default;
464
465 template<class L>
466 void wait (L & lock)
467 {
468 wait_impl(lock, INFINITE);
469 }
470
471 template<class L, class Predicate>
472 void wait (L & lock, Predicate pred)
473 {
474 while (!pred())
475 wait(lock);
476 }
477
478 template <class L, class Rep, class Period>
479 cv_status wait_for(L& lock, const std::chrono::duration<Rep,Period>& period)
480 {
481 using namespace std::chrono;
482 auto timeout = duration_cast<milliseconds>(period).count();
483 DWORD waittime = (timeout < INFINITE) ? ((timeout < 0) ? 0 : static_cast<DWORD>(timeout)) : (INFINITE - 1);
484 bool result = wait_impl(lock, waittime) || (timeout >= INFINITE);
485 return result ? cv_status::no_timeout : cv_status::timeout;
486 }
487
488 template <class L, class Rep, class Period, class Predicate>
489 bool wait_for(L& lock, const std::chrono::duration<Rep, Period>& period,
490 Predicate pred)
491 {
492 return wait_until(lock, std::chrono::steady_clock::now() + period,
493 std::move(pred));
494 }
495 template <class L, class Clock, class Duration>
496 cv_status wait_until (L& lock,
497 const std::chrono::time_point<Clock,Duration>& abs_time)
498 {
499 return wait_for(lock, abs_time - Clock::now());
500 }
501 template <class L, class Clock, class Duration, class Predicate>
502 bool wait_until (L& lock,
503 const std::chrono::time_point<Clock, Duration>& abs_time,
504 Predicate pred)
505 {
506 while (!pred())
507 {
508 if (wait_until(lock, abs_time) == cv_status::timeout)
509 {
510 return pred();
511 }
512 }
513 return true;
514 }
515};
516} // Namespace vista
517#endif
518#if WINVER < 0x0600
519using xp::condition_variable;
520using xp::condition_variable_any;
521#else
522using vista::condition_variable;
523using vista::condition_variable_any;
524#endif
525} // Namespace mingw_stdthread
526
527// Push objects into std, but only if they are not already there.
528namespace std
529{
530// Because of quirks of the compiler, the common "using namespace std;"
531// directive would flatten the namespaces and introduce ambiguity where there
532// was none. Direct specification (std::), however, would be unaffected.
533// Take the safe option, and include only in the presence of MinGW's win32
534// implementation.
535#if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS)
536using mingw_stdthread::cv_status;
537using mingw_stdthread::condition_variable;
538using mingw_stdthread::condition_variable_any;
539#elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING) // Skip repetition
540#define MINGW_STDTHREAD_REDUNDANCY_WARNING
541#pragma message "This version of MinGW seems to include a win32 port of\
542 pthreads, and probably already has C++11 std threading classes implemented,\
543 based on pthreads. These classes, found in namespace std, are not overridden\
544 by the mingw-std-thread library. If you would still like to use this\
545 implementation (as it is more lightweight), use the classes provided in\
546 namespace mingw_stdthread."
547#endif
548}
549#endif // MINGW_CONDITIONAL_VARIABLE_H