src/third-party/discord-rpc/include/mingw-std-threads/mutex (view raw)
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_next <mutex> //need for call_once()
41
42#if STDMUTEX_RECURSION_CHECKS || !defined(NDEBUG)
43#include <cstdio>
44#endif
45
46#include <windows.h>
47
48// Need for the implementation of invoke
49#include <thread>
50
51#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0501)
52#error To use the MinGW-std-threads library, you will need to define the macro _WIN32_WINNT to be 0x0501 (Windows XP) or higher.
53#endif
54
55namespace mingw_stdthread
56{
57// The _NonRecursive class has mechanisms that do not play nice with direct
58// manipulation of the native handle. This forward declaration is part of
59// a friend class declaration.
60#if STDMUTEX_RECURSION_CHECKS
61namespace vista
62{
63class condition_variable;
64}
65#endif
66// To make this namespace equivalent to the thread-related subset of std,
67// pull in the classes and class templates supplied by std but not by this
68// implementation.
69using std::lock_guard;
70using std::unique_lock;
71using std::adopt_lock_t;
72using std::defer_lock_t;
73using std::try_to_lock_t;
74using std::adopt_lock;
75using std::defer_lock;
76using std::try_to_lock;
77
78class recursive_mutex
79{
80 CRITICAL_SECTION mHandle;
81public:
82 typedef LPCRITICAL_SECTION native_handle_type;
83 native_handle_type native_handle() {return &mHandle;}
84 recursive_mutex() noexcept : mHandle()
85 {
86 InitializeCriticalSection(&mHandle);
87 }
88 recursive_mutex (const recursive_mutex&) = delete;
89 recursive_mutex& operator=(const recursive_mutex&) = delete;
90 ~recursive_mutex() noexcept
91 {
92 DeleteCriticalSection(&mHandle);
93 }
94 void lock()
95 {
96 EnterCriticalSection(&mHandle);
97 }
98 void unlock()
99 {
100 LeaveCriticalSection(&mHandle);
101 }
102 bool try_lock()
103 {
104 return (TryEnterCriticalSection(&mHandle)!=0);
105 }
106};
107
108#if STDMUTEX_RECURSION_CHECKS
109struct _OwnerThread
110{
111// If this is to be read before locking, then the owner-thread variable must
112// be atomic to prevent a torn read from spuriously causing errors.
113 std::atomic<DWORD> mOwnerThread;
114 constexpr _OwnerThread () noexcept : mOwnerThread(0) {}
115 static void on_deadlock (void)
116 {
117 using namespace std;
118 fprintf(stderr, "FATAL: Recursive locking of non-recursive mutex\
119 detected. Throwing system exception\n");
120 fflush(stderr);
121 throw system_error(make_error_code(errc::resource_deadlock_would_occur));
122 }
123 DWORD checkOwnerBeforeLock() const
124 {
125 DWORD self = GetCurrentThreadId();
126 if (mOwnerThread.load(std::memory_order_relaxed) == self)
127 on_deadlock();
128 return self;
129 }
130 void setOwnerAfterLock(DWORD id)
131 {
132 mOwnerThread.store(id, std::memory_order_relaxed);
133 }
134 void checkSetOwnerBeforeUnlock()
135 {
136 DWORD self = GetCurrentThreadId();
137 if (mOwnerThread.load(std::memory_order_relaxed) != self)
138 on_deadlock();
139 mOwnerThread.store(0, std::memory_order_relaxed);
140 }
141};
142#endif
143
144// Though the Slim Reader-Writer (SRW) locks used here are not complete until
145// Windows 7, implementing partial functionality in Vista will simplify the
146// interaction with condition variables.
147#if defined(_WIN32) && (WINVER >= _WIN32_WINNT_VISTA)
148namespace windows7
149{
150class mutex
151{
152 SRWLOCK mHandle;
153// Track locking thread for error checking.
154#if STDMUTEX_RECURSION_CHECKS
155 friend class vista::condition_variable;
156 _OwnerThread mOwnerThread {};
157#endif
158public:
159 typedef PSRWLOCK native_handle_type;
160#pragma GCC diagnostic push
161#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
162 constexpr mutex () noexcept : mHandle(SRWLOCK_INIT) { }
163#pragma GCC diagnostic pop
164 mutex (const mutex&) = delete;
165 mutex & operator= (const mutex&) = delete;
166 void lock (void)
167 {
168// Note: Undefined behavior if called recursively.
169#if STDMUTEX_RECURSION_CHECKS
170 DWORD self = mOwnerThread.checkOwnerBeforeLock();
171#endif
172 AcquireSRWLockExclusive(&mHandle);
173#if STDMUTEX_RECURSION_CHECKS
174 mOwnerThread.setOwnerAfterLock(self);
175#endif
176 }
177 void unlock (void)
178 {
179#if STDMUTEX_RECURSION_CHECKS
180 mOwnerThread.checkSetOwnerBeforeUnlock();
181#endif
182 ReleaseSRWLockExclusive(&mHandle);
183 }
184// TryAcquireSRW functions are a Windows 7 feature.
185#if (WINVER >= _WIN32_WINNT_WIN7)
186 bool try_lock (void)
187 {
188#if STDMUTEX_RECURSION_CHECKS
189 DWORD self = mOwnerThread.checkOwnerBeforeLock();
190#endif
191 BOOL ret = TryAcquireSRWLockExclusive(&mHandle);
192#if STDMUTEX_RECURSION_CHECKS
193 if (ret)
194 mOwnerThread.setOwnerAfterLock(self);
195#endif
196 return ret;
197 }
198#endif
199 native_handle_type native_handle (void)
200 {
201 return &mHandle;
202 }
203};
204} // Namespace windows7
205#endif // Compiling for Vista
206namespace xp
207{
208class mutex
209{
210 CRITICAL_SECTION mHandle;
211 std::atomic_uchar mState;
212// Track locking thread for error checking.
213#if STDMUTEX_RECURSION_CHECKS
214 friend class vista::condition_variable;
215 _OwnerThread mOwnerThread {};
216#endif
217public:
218 typedef PCRITICAL_SECTION native_handle_type;
219 constexpr mutex () noexcept : mHandle(), mState(2) { }
220 mutex (const mutex&) = delete;
221 mutex & operator= (const mutex&) = delete;
222 ~mutex() noexcept
223 {
224// Undefined behavior if the mutex is held (locked) by any thread.
225// Undefined behavior if a thread terminates while holding ownership of the
226// mutex.
227 DeleteCriticalSection(&mHandle);
228 }
229 void lock (void)
230 {
231 unsigned char state = mState.load(std::memory_order_acquire);
232 while (state) {
233 if ((state == 2) && mState.compare_exchange_weak(state, 1, std::memory_order_acquire))
234 {
235 InitializeCriticalSection(&mHandle);
236 mState.store(0, std::memory_order_release);
237 break;
238 }
239 if (state == 1)
240 {
241 Sleep(0);
242 state = mState.load(std::memory_order_acquire);
243 }
244 }
245#if STDMUTEX_RECURSION_CHECKS
246 DWORD self = mOwnerThread.checkOwnerBeforeLock();
247#endif
248 EnterCriticalSection(&mHandle);
249#if STDMUTEX_RECURSION_CHECKS
250 mOwnerThread.setOwnerAfterLock(self);
251#endif
252 }
253 void unlock (void)
254 {
255#if STDMUTEX_RECURSION_CHECKS
256 mOwnerThread.checkSetOwnerBeforeUnlock();
257#endif
258 LeaveCriticalSection(&mHandle);
259 }
260 bool try_lock (void)
261 {
262 unsigned char state = mState.load(std::memory_order_acquire);
263 if ((state == 2) && mState.compare_exchange_strong(state, 1, std::memory_order_acquire))
264 {
265 InitializeCriticalSection(&mHandle);
266 mState.store(0, std::memory_order_release);
267 }
268 if (state == 1)
269 return false;
270#if STDMUTEX_RECURSION_CHECKS
271 DWORD self = mOwnerThread.checkOwnerBeforeLock();
272#endif
273 BOOL ret = TryEnterCriticalSection(&mHandle);
274#if STDMUTEX_RECURSION_CHECKS
275 if (ret)
276 mOwnerThread.setOwnerAfterLock(self);
277#endif
278 return ret;
279 }
280 native_handle_type native_handle (void)
281 {
282 return &mHandle;
283 }
284};
285} // Namespace "xp"
286#if (WINVER >= _WIN32_WINNT_WIN7)
287using windows7::mutex;
288#else
289using xp::mutex;
290#endif
291
292class recursive_timed_mutex
293{
294 inline bool try_lock_internal (DWORD ms) noexcept
295 {
296 DWORD ret = WaitForSingleObject(mHandle, ms);
297#ifndef NDEBUG
298 if (ret == WAIT_ABANDONED)
299 {
300 using namespace std;
301 fprintf(stderr, "FATAL: Thread terminated while holding a mutex.");
302 terminate();
303 }
304#endif
305 return (ret == WAIT_OBJECT_0) || (ret == WAIT_ABANDONED);
306 }
307protected:
308 HANDLE mHandle;
309// Track locking thread for error checking of non-recursive timed_mutex. For
310// standard compliance, this must be defined in same class and at the same
311// access-control level as every other variable in the timed_mutex.
312#if STDMUTEX_RECURSION_CHECKS
313 friend class vista::condition_variable;
314 _OwnerThread mOwnerThread {};
315#endif
316public:
317 typedef HANDLE native_handle_type;
318 native_handle_type native_handle() const {return mHandle;}
319 recursive_timed_mutex(const recursive_timed_mutex&) = delete;
320 recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete;
321 recursive_timed_mutex(): mHandle(CreateMutex(NULL, FALSE, NULL)) {}
322 ~recursive_timed_mutex()
323 {
324 CloseHandle(mHandle);
325 }
326 void lock()
327 {
328 DWORD ret = WaitForSingleObject(mHandle, INFINITE);
329// If (ret == WAIT_ABANDONED), then the thread that held ownership was
330// terminated. Behavior is undefined, but Windows will pass ownership to this
331// thread.
332#ifndef NDEBUG
333 if (ret == WAIT_ABANDONED)
334 {
335 using namespace std;
336 fprintf(stderr, "FATAL: Thread terminated while holding a mutex.");
337 terminate();
338 }
339#endif
340 if ((ret != WAIT_OBJECT_0) && (ret != WAIT_ABANDONED))
341 {
342 throw std::system_error(GetLastError(), std::system_category());
343 }
344 }
345 void unlock()
346 {
347 if (!ReleaseMutex(mHandle))
348 throw std::system_error(GetLastError(), std::system_category());
349 }
350 bool try_lock()
351 {
352 return try_lock_internal(0);
353 }
354 template <class Rep, class Period>
355 bool try_lock_for(const std::chrono::duration<Rep,Period>& dur)
356 {
357 using namespace std::chrono;
358 auto timeout = duration_cast<milliseconds>(dur).count();
359 while (timeout > 0)
360 {
361 constexpr auto kMaxStep = static_cast<decltype(timeout)>(INFINITE-1);
362 auto step = (timeout < kMaxStep) ? timeout : kMaxStep;
363 if (try_lock_internal(static_cast<DWORD>(step)))
364 return true;
365 timeout -= step;
366 }
367 return false;
368 }
369 template <class Clock, class Duration>
370 bool try_lock_until(const std::chrono::time_point<Clock,Duration>& timeout_time)
371 {
372 return try_lock_for(timeout_time - Clock::now());
373 }
374};
375
376// Override if, and only if, it is necessary for error-checking.
377#if STDMUTEX_RECURSION_CHECKS
378class timed_mutex: recursive_timed_mutex
379{
380public:
381 timed_mutex(const timed_mutex&) = delete;
382 timed_mutex& operator=(const timed_mutex&) = delete;
383 void lock()
384 {
385 DWORD self = mOwnerThread.checkOwnerBeforeLock();
386 recursive_timed_mutex::lock();
387 mOwnerThread.setOwnerAfterLock(self);
388 }
389 void unlock()
390 {
391 mOwnerThread.checkSetOwnerBeforeUnlock();
392 recursive_timed_mutex::unlock();
393 }
394 template <class Rep, class Period>
395 bool try_lock_for(const std::chrono::duration<Rep,Period>& dur)
396 {
397 DWORD self = mOwnerThread.checkOwnerBeforeLock();
398 bool ret = recursive_timed_mutex::try_lock_for(dur);
399 if (ret)
400 mOwnerThread.setOwnerAfterLock(self);
401 return ret;
402 }
403 template <class Clock, class Duration>
404 bool try_lock_until(const std::chrono::time_point<Clock,Duration>& timeout_time)
405 {
406 return try_lock_for(timeout_time - Clock::now());
407 }
408 bool try_lock ()
409 {
410 return try_lock_for(std::chrono::milliseconds(0));
411 }
412};
413#else
414typedef recursive_timed_mutex timed_mutex;
415#endif
416
417class once_flag
418{
419// When available, the SRW-based mutexes should be faster than the
420// CriticalSection-based mutexes. Only try_lock will be unavailable in Vista,
421// and try_lock is not used by once_flag.
422#if (_WIN32_WINNT == _WIN32_WINNT_VISTA)
423 windows7::mutex mMutex;
424#else
425 mutex mMutex;
426#endif
427 std::atomic_bool mHasRun;
428 once_flag(const once_flag&) = delete;
429 once_flag& operator=(const once_flag&) = delete;
430 template<class Callable, class... Args>
431 friend void call_once(once_flag& once, Callable&& f, Args&&... args);
432public:
433 constexpr once_flag() noexcept: mMutex(), mHasRun(false) {}
434};
435
436template<class Callable, class... Args>
437void call_once(once_flag& flag, Callable&& func, Args&&... args)
438{
439 if (flag.mHasRun.load(std::memory_order_acquire))
440 return;
441 lock_guard<decltype(flag.mMutex)> lock(flag.mMutex);
442 if (flag.mHasRun.load(std::memory_order_acquire))
443 return;
444 detail::invoke(std::forward<Callable>(func),std::forward<Args>(args)...);
445 flag.mHasRun.store(true, std::memory_order_release);
446}
447} // Namespace mingw_stdthread
448
449// Push objects into std, but only if they are not already there.
450namespace std
451{
452// Because of quirks of the compiler, the common "using namespace std;"
453// directive would flatten the namespaces and introduce ambiguity where there
454// was none. Direct specification (std::), however, would be unaffected.
455// Take the safe option, and include only in the presence of MinGW's win32
456// implementation.
457#if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS)
458using mingw_stdthread::recursive_mutex;
459using mingw_stdthread::mutex;
460using mingw_stdthread::recursive_timed_mutex;
461using mingw_stdthread::timed_mutex;
462using mingw_stdthread::once_flag;
463using mingw_stdthread::call_once;
464#elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING) // Skip repetition
465#define MINGW_STDTHREAD_REDUNDANCY_WARNING
466#pragma message "This version of MinGW seems to include a win32 port of\
467 pthreads, and probably already has C++11 std threading classes implemented,\
468 based on pthreads. These classes, found in namespace std, are not overridden\
469 by the mingw-std-thread library. If you would still like to use this\
470 implementation (as it is more lightweight), use the classes provided in\
471 namespace mingw_stdthread."
472#endif
473}
474#endif // WIN32STDMUTEX_H