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