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