Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/mingw-std-threads/mingw.mutex.h
21431 views
1
/**
2
* @file mingw.mutex.h
3
* @brief std::mutex et al implementation for MinGW
4
** (c) 2013-2016 by Mega Limited, Auckland, New Zealand
5
* @author Alexander Vassilev
6
*
7
* @copyright Simplified (2-clause) BSD License.
8
* You should have received a copy of the license along with this
9
* program.
10
*
11
* This code is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
* @note
15
* This file may become part of the mingw-w64 runtime package. If/when this happens,
16
* the appropriate license will be added, i.e. this code will become dual-licensed,
17
* and the current BSD 2-clause license will stay.
18
*/
19
20
#ifndef WIN32STDMUTEX_H
21
#define WIN32STDMUTEX_H
22
23
#if !defined(__cplusplus) || (__cplusplus < 201103L)
24
#error A C++11 compiler is required!
25
#endif
26
// Recursion checks on non-recursive locks have some performance penalty, and
27
// the C++ standard does not mandate them. The user might want to explicitly
28
// enable or disable such checks. If the user has no preference, enable such
29
// checks in debug builds, but not in release builds.
30
#ifdef STDMUTEX_RECURSION_CHECKS
31
#elif defined(NDEBUG)
32
#define STDMUTEX_RECURSION_CHECKS 0
33
#else
34
#define STDMUTEX_RECURSION_CHECKS 1
35
#endif
36
37
#include <chrono>
38
#include <system_error>
39
#include <atomic>
40
#include <exception>
41
#include <mutex> //need for call_once()
42
43
#if STDMUTEX_RECURSION_CHECKS || !defined(NDEBUG)
44
#include <cstdio>
45
#endif
46
47
#include <sdkddkver.h> // Detect Windows version.
48
49
#if (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))
50
#pragma message "The Windows API that MinGW-w32 provides is not fully compatible\
51
with Microsoft's API. We'll try to work around this, but we can make no\
52
guarantees. This problem does not exist in MinGW-w64."
53
#include <windows.h> // No further granularity can be expected.
54
#else
55
#if STDMUTEX_RECURSION_CHECKS
56
#include <processthreadsapi.h> // For GetCurrentThreadId
57
#endif
58
#include <synchapi.h> // For InitializeCriticalSection, etc.
59
#include <errhandlingapi.h> // For GetLastError
60
#include <handleapi.h>
61
#endif
62
63
// Need for the implementation of invoke
64
#include "mingw.invoke.h"
65
66
#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0501)
67
#error To use the MinGW-std-threads library, you will need to define the macro _WIN32_WINNT to be 0x0501 (Windows XP) or higher.
68
#endif
69
70
namespace mingw_stdthread
71
{
72
// The _NonRecursive class has mechanisms that do not play nice with direct
73
// manipulation of the native handle. This forward declaration is part of
74
// a friend class declaration.
75
#if STDMUTEX_RECURSION_CHECKS
76
namespace vista
77
{
78
class condition_variable;
79
}
80
#endif
81
// To make this namespace equivalent to the thread-related subset of std,
82
// pull in the classes and class templates supplied by std but not by this
83
// implementation.
84
using std::lock_guard;
85
using std::unique_lock;
86
using std::adopt_lock_t;
87
using std::defer_lock_t;
88
using std::try_to_lock_t;
89
using std::adopt_lock;
90
using std::defer_lock;
91
using std::try_to_lock;
92
93
class recursive_mutex
94
{
95
CRITICAL_SECTION mHandle;
96
public:
97
typedef LPCRITICAL_SECTION native_handle_type;
98
native_handle_type native_handle() {return &mHandle;}
99
recursive_mutex() noexcept : mHandle()
100
{
101
InitializeCriticalSection(&mHandle);
102
}
103
recursive_mutex (const recursive_mutex&) = delete;
104
recursive_mutex& operator=(const recursive_mutex&) = delete;
105
~recursive_mutex() noexcept
106
{
107
DeleteCriticalSection(&mHandle);
108
}
109
void lock()
110
{
111
EnterCriticalSection(&mHandle);
112
}
113
void unlock()
114
{
115
LeaveCriticalSection(&mHandle);
116
}
117
bool try_lock()
118
{
119
return (TryEnterCriticalSection(&mHandle)!=0);
120
}
121
};
122
123
#if STDMUTEX_RECURSION_CHECKS
124
struct _OwnerThread
125
{
126
// If this is to be read before locking, then the owner-thread variable must
127
// be atomic to prevent a torn read from spuriously causing errors.
128
std::atomic<DWORD> mOwnerThread;
129
constexpr _OwnerThread () noexcept : mOwnerThread(0) {}
130
static void on_deadlock (void)
131
{
132
using namespace std;
133
fprintf(stderr, "FATAL: Recursive locking of non-recursive mutex\
134
detected. Throwing system exception\n");
135
fflush(stderr);
136
__builtin_trap();
137
}
138
DWORD checkOwnerBeforeLock() const
139
{
140
DWORD self = GetCurrentThreadId();
141
if (mOwnerThread.load(std::memory_order_relaxed) == self)
142
on_deadlock();
143
return self;
144
}
145
void setOwnerAfterLock(DWORD id)
146
{
147
mOwnerThread.store(id, std::memory_order_relaxed);
148
}
149
void checkSetOwnerBeforeUnlock()
150
{
151
DWORD self = GetCurrentThreadId();
152
if (mOwnerThread.load(std::memory_order_relaxed) != self)
153
on_deadlock();
154
mOwnerThread.store(0, std::memory_order_relaxed);
155
}
156
};
157
#endif
158
159
// Though the Slim Reader-Writer (SRW) locks used here are not complete until
160
// Windows 7, implementing partial functionality in Vista will simplify the
161
// interaction with condition variables.
162
163
//Define SRWLOCK_INIT.
164
165
#if !defined(SRWLOCK_INIT)
166
#pragma message "SRWLOCK_INIT macro is not defined. Defining automatically."
167
#define SRWLOCK_INIT {0}
168
#endif
169
170
#if defined(_WIN32) && (WINVER >= _WIN32_WINNT_VISTA)
171
namespace windows7
172
{
173
class mutex
174
{
175
SRWLOCK mHandle;
176
// Track locking thread for error checking.
177
#if STDMUTEX_RECURSION_CHECKS
178
friend class vista::condition_variable;
179
_OwnerThread mOwnerThread {};
180
#endif
181
public:
182
typedef PSRWLOCK native_handle_type;
183
#pragma GCC diagnostic push
184
#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
185
constexpr mutex () noexcept : mHandle(SRWLOCK_INIT) { }
186
#pragma GCC diagnostic pop
187
mutex (const mutex&) = delete;
188
mutex & operator= (const mutex&) = delete;
189
void lock (void)
190
{
191
// Note: Undefined behavior if called recursively.
192
#if STDMUTEX_RECURSION_CHECKS
193
DWORD self = mOwnerThread.checkOwnerBeforeLock();
194
#endif
195
AcquireSRWLockExclusive(&mHandle);
196
#if STDMUTEX_RECURSION_CHECKS
197
mOwnerThread.setOwnerAfterLock(self);
198
#endif
199
}
200
void unlock (void)
201
{
202
#if STDMUTEX_RECURSION_CHECKS
203
mOwnerThread.checkSetOwnerBeforeUnlock();
204
#endif
205
ReleaseSRWLockExclusive(&mHandle);
206
}
207
// TryAcquireSRW functions are a Windows 7 feature.
208
#if (WINVER >= _WIN32_WINNT_WIN7)
209
bool try_lock (void)
210
{
211
#if STDMUTEX_RECURSION_CHECKS
212
DWORD self = mOwnerThread.checkOwnerBeforeLock();
213
#endif
214
BOOL ret = TryAcquireSRWLockExclusive(&mHandle);
215
#if STDMUTEX_RECURSION_CHECKS
216
if (ret)
217
mOwnerThread.setOwnerAfterLock(self);
218
#endif
219
return ret;
220
}
221
#endif
222
native_handle_type native_handle (void)
223
{
224
return &mHandle;
225
}
226
};
227
} // Namespace windows7
228
#endif // Compiling for Vista
229
namespace xp
230
{
231
class mutex
232
{
233
CRITICAL_SECTION mHandle;
234
std::atomic_uchar mState;
235
// Track locking thread for error checking.
236
#if STDMUTEX_RECURSION_CHECKS
237
friend class vista::condition_variable;
238
_OwnerThread mOwnerThread {};
239
#endif
240
public:
241
typedef PCRITICAL_SECTION native_handle_type;
242
constexpr mutex () noexcept : mHandle(), mState(2) { }
243
mutex (const mutex&) = delete;
244
mutex & operator= (const mutex&) = delete;
245
~mutex() noexcept
246
{
247
// Undefined behavior if the mutex is held (locked) by any thread.
248
// Undefined behavior if a thread terminates while holding ownership of the
249
// mutex.
250
DeleteCriticalSection(&mHandle);
251
}
252
void lock (void)
253
{
254
unsigned char state = mState.load(std::memory_order_acquire);
255
while (state) {
256
if ((state == 2) && mState.compare_exchange_weak(state, 1, std::memory_order_acquire))
257
{
258
InitializeCriticalSection(&mHandle);
259
mState.store(0, std::memory_order_release);
260
break;
261
}
262
if (state == 1)
263
{
264
Sleep(0);
265
state = mState.load(std::memory_order_acquire);
266
}
267
}
268
#if STDMUTEX_RECURSION_CHECKS
269
DWORD self = mOwnerThread.checkOwnerBeforeLock();
270
#endif
271
EnterCriticalSection(&mHandle);
272
#if STDMUTEX_RECURSION_CHECKS
273
mOwnerThread.setOwnerAfterLock(self);
274
#endif
275
}
276
void unlock (void)
277
{
278
#if STDMUTEX_RECURSION_CHECKS
279
mOwnerThread.checkSetOwnerBeforeUnlock();
280
#endif
281
LeaveCriticalSection(&mHandle);
282
}
283
bool try_lock (void)
284
{
285
unsigned char state = mState.load(std::memory_order_acquire);
286
if ((state == 2) && mState.compare_exchange_strong(state, 1, std::memory_order_acquire))
287
{
288
InitializeCriticalSection(&mHandle);
289
mState.store(0, std::memory_order_release);
290
}
291
if (state == 1)
292
return false;
293
#if STDMUTEX_RECURSION_CHECKS
294
DWORD self = mOwnerThread.checkOwnerBeforeLock();
295
#endif
296
BOOL ret = TryEnterCriticalSection(&mHandle);
297
#if STDMUTEX_RECURSION_CHECKS
298
if (ret)
299
mOwnerThread.setOwnerAfterLock(self);
300
#endif
301
return ret;
302
}
303
native_handle_type native_handle (void)
304
{
305
return &mHandle;
306
}
307
};
308
} // Namespace "xp"
309
#if (WINVER >= _WIN32_WINNT_WIN7)
310
using windows7::mutex;
311
#else
312
using xp::mutex;
313
#endif
314
315
class recursive_timed_mutex
316
{
317
static constexpr DWORD kWaitAbandoned = 0x00000080l;
318
static constexpr DWORD kWaitObject0 = 0x00000000l;
319
static constexpr DWORD kInfinite = 0xffffffffl;
320
inline bool try_lock_internal (DWORD ms) noexcept
321
{
322
DWORD ret = WaitForSingleObject(mHandle, ms);
323
#ifndef NDEBUG
324
if (ret == kWaitAbandoned)
325
{
326
using namespace std;
327
fprintf(stderr, "FATAL: Thread terminated while holding a mutex.");
328
terminate();
329
}
330
#endif
331
return (ret == kWaitObject0) || (ret == kWaitAbandoned);
332
}
333
protected:
334
HANDLE mHandle;
335
// Track locking thread for error checking of non-recursive timed_mutex. For
336
// standard compliance, this must be defined in same class and at the same
337
// access-control level as every other variable in the timed_mutex.
338
#if STDMUTEX_RECURSION_CHECKS
339
friend class vista::condition_variable;
340
_OwnerThread mOwnerThread {};
341
#endif
342
public:
343
typedef HANDLE native_handle_type;
344
native_handle_type native_handle() const {return mHandle;}
345
recursive_timed_mutex(const recursive_timed_mutex&) = delete;
346
recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete;
347
recursive_timed_mutex(): mHandle(CreateMutex(NULL, FALSE, NULL)) {}
348
~recursive_timed_mutex()
349
{
350
CloseHandle(mHandle);
351
}
352
void lock()
353
{
354
DWORD ret = WaitForSingleObject(mHandle, kInfinite);
355
// If (ret == WAIT_ABANDONED), then the thread that held ownership was
356
// terminated. Behavior is undefined, but Windows will pass ownership to this
357
// thread.
358
#ifndef NDEBUG
359
if (ret == kWaitAbandoned)
360
{
361
using namespace std;
362
fprintf(stderr, "FATAL: Thread terminated while holding a mutex.");
363
terminate();
364
}
365
#endif
366
if ((ret != kWaitObject0) && (ret != kWaitAbandoned))
367
{
368
__builtin_trap();
369
}
370
}
371
void unlock()
372
{
373
if (!ReleaseMutex(mHandle))
374
__builtin_trap();
375
}
376
bool try_lock()
377
{
378
return try_lock_internal(0);
379
}
380
template <class Rep, class Period>
381
bool try_lock_for(const std::chrono::duration<Rep,Period>& dur)
382
{
383
using namespace std::chrono;
384
auto timeout = duration_cast<milliseconds>(dur).count();
385
while (timeout > 0)
386
{
387
constexpr auto kMaxStep = static_cast<decltype(timeout)>(kInfinite-1);
388
auto step = (timeout < kMaxStep) ? timeout : kMaxStep;
389
if (try_lock_internal(static_cast<DWORD>(step)))
390
return true;
391
timeout -= step;
392
}
393
return false;
394
}
395
template <class Clock, class Duration>
396
bool try_lock_until(const std::chrono::time_point<Clock,Duration>& timeout_time)
397
{
398
return try_lock_for(timeout_time - Clock::now());
399
}
400
};
401
402
// Override if, and only if, it is necessary for error-checking.
403
#if STDMUTEX_RECURSION_CHECKS
404
class timed_mutex: recursive_timed_mutex
405
{
406
public:
407
timed_mutex() = default;
408
timed_mutex(const timed_mutex&) = delete;
409
timed_mutex& operator=(const timed_mutex&) = delete;
410
void lock()
411
{
412
DWORD self = mOwnerThread.checkOwnerBeforeLock();
413
recursive_timed_mutex::lock();
414
mOwnerThread.setOwnerAfterLock(self);
415
}
416
void unlock()
417
{
418
mOwnerThread.checkSetOwnerBeforeUnlock();
419
recursive_timed_mutex::unlock();
420
}
421
template <class Rep, class Period>
422
bool try_lock_for(const std::chrono::duration<Rep,Period>& dur)
423
{
424
DWORD self = mOwnerThread.checkOwnerBeforeLock();
425
bool ret = recursive_timed_mutex::try_lock_for(dur);
426
if (ret)
427
mOwnerThread.setOwnerAfterLock(self);
428
return ret;
429
}
430
template <class Clock, class Duration>
431
bool try_lock_until(const std::chrono::time_point<Clock,Duration>& timeout_time)
432
{
433
return try_lock_for(timeout_time - Clock::now());
434
}
435
bool try_lock ()
436
{
437
return try_lock_for(std::chrono::milliseconds(0));
438
}
439
};
440
#else
441
typedef recursive_timed_mutex timed_mutex;
442
#endif
443
444
class once_flag
445
{
446
// When available, the SRW-based mutexes should be faster than the
447
// CriticalSection-based mutexes. Only try_lock will be unavailable in Vista,
448
// and try_lock is not used by once_flag.
449
#if (_WIN32_WINNT == _WIN32_WINNT_VISTA)
450
windows7::mutex mMutex;
451
#else
452
mutex mMutex;
453
#endif
454
std::atomic_bool mHasRun;
455
once_flag(const once_flag&) = delete;
456
once_flag& operator=(const once_flag&) = delete;
457
template<class Callable, class... Args>
458
friend void call_once(once_flag& once, Callable&& f, Args&&... args);
459
public:
460
constexpr once_flag() noexcept: mMutex(), mHasRun(false) {}
461
};
462
463
template<class Callable, class... Args>
464
void call_once(once_flag& flag, Callable&& func, Args&&... args)
465
{
466
if (flag.mHasRun.load(std::memory_order_acquire))
467
return;
468
lock_guard<decltype(flag.mMutex)> lock(flag.mMutex);
469
if (flag.mHasRun.load(std::memory_order_relaxed))
470
return;
471
detail::invoke(std::forward<Callable>(func),std::forward<Args>(args)...);
472
flag.mHasRun.store(true, std::memory_order_release);
473
}
474
} // Namespace mingw_stdthread
475
476
// Push objects into std, but only if they are not already there.
477
namespace std
478
{
479
// Because of quirks of the compiler, the common "using namespace std;"
480
// directive would flatten the namespaces and introduce ambiguity where there
481
// was none. Direct specification (std::), however, would be unaffected.
482
// Take the safe option, and include only in the presence of MinGW's win32
483
// implementation.
484
#if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS) && !defined(__clang__)
485
using mingw_stdthread::recursive_mutex;
486
using mingw_stdthread::mutex;
487
using mingw_stdthread::recursive_timed_mutex;
488
using mingw_stdthread::timed_mutex;
489
using mingw_stdthread::once_flag;
490
using mingw_stdthread::call_once;
491
#elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING) // Skip repetition
492
#define MINGW_STDTHREAD_REDUNDANCY_WARNING
493
#pragma message "This version of MinGW seems to include a win32 port of\
494
pthreads, and probably already has C++11 std threading classes implemented,\
495
based on pthreads. These classes, found in namespace std, are not overridden\
496
by the mingw-std-thread library. If you would still like to use this\
497
implementation (as it is more lightweight), use the classes provided in\
498
namespace mingw_stdthread."
499
#endif
500
}
501
#endif // WIN32STDMUTEX_H
502
503