Path: blob/master/thirdparty/mingw-std-threads/mingw.mutex.h
21431 views
/**1* @file mingw.mutex.h2* @brief std::mutex et al implementation for MinGW3** (c) 2013-2016 by Mega Limited, Auckland, New Zealand4* @author Alexander Vassilev5*6* @copyright Simplified (2-clause) BSD License.7* You should have received a copy of the license along with this8* program.9*10* This code is distributed in the hope that it will be useful,11* but WITHOUT ANY WARRANTY; without even the implied warranty of12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.13* @note14* This file may become part of the mingw-w64 runtime package. If/when this happens,15* the appropriate license will be added, i.e. this code will become dual-licensed,16* and the current BSD 2-clause license will stay.17*/1819#ifndef WIN32STDMUTEX_H20#define WIN32STDMUTEX_H2122#if !defined(__cplusplus) || (__cplusplus < 201103L)23#error A C++11 compiler is required!24#endif25// Recursion checks on non-recursive locks have some performance penalty, and26// the C++ standard does not mandate them. The user might want to explicitly27// enable or disable such checks. If the user has no preference, enable such28// checks in debug builds, but not in release builds.29#ifdef STDMUTEX_RECURSION_CHECKS30#elif defined(NDEBUG)31#define STDMUTEX_RECURSION_CHECKS 032#else33#define STDMUTEX_RECURSION_CHECKS 134#endif3536#include <chrono>37#include <system_error>38#include <atomic>39#include <exception>40#include <mutex> //need for call_once()4142#if STDMUTEX_RECURSION_CHECKS || !defined(NDEBUG)43#include <cstdio>44#endif4546#include <sdkddkver.h> // Detect Windows version.4748#if (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))49#pragma message "The Windows API that MinGW-w32 provides is not fully compatible\50with Microsoft's API. We'll try to work around this, but we can make no\51guarantees. This problem does not exist in MinGW-w64."52#include <windows.h> // No further granularity can be expected.53#else54#if STDMUTEX_RECURSION_CHECKS55#include <processthreadsapi.h> // For GetCurrentThreadId56#endif57#include <synchapi.h> // For InitializeCriticalSection, etc.58#include <errhandlingapi.h> // For GetLastError59#include <handleapi.h>60#endif6162// Need for the implementation of invoke63#include "mingw.invoke.h"6465#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0501)66#error To use the MinGW-std-threads library, you will need to define the macro _WIN32_WINNT to be 0x0501 (Windows XP) or higher.67#endif6869namespace mingw_stdthread70{71// The _NonRecursive class has mechanisms that do not play nice with direct72// manipulation of the native handle. This forward declaration is part of73// a friend class declaration.74#if STDMUTEX_RECURSION_CHECKS75namespace vista76{77class condition_variable;78}79#endif80// To make this namespace equivalent to the thread-related subset of std,81// pull in the classes and class templates supplied by std but not by this82// implementation.83using std::lock_guard;84using std::unique_lock;85using std::adopt_lock_t;86using std::defer_lock_t;87using std::try_to_lock_t;88using std::adopt_lock;89using std::defer_lock;90using std::try_to_lock;9192class recursive_mutex93{94CRITICAL_SECTION mHandle;95public:96typedef LPCRITICAL_SECTION native_handle_type;97native_handle_type native_handle() {return &mHandle;}98recursive_mutex() noexcept : mHandle()99{100InitializeCriticalSection(&mHandle);101}102recursive_mutex (const recursive_mutex&) = delete;103recursive_mutex& operator=(const recursive_mutex&) = delete;104~recursive_mutex() noexcept105{106DeleteCriticalSection(&mHandle);107}108void lock()109{110EnterCriticalSection(&mHandle);111}112void unlock()113{114LeaveCriticalSection(&mHandle);115}116bool try_lock()117{118return (TryEnterCriticalSection(&mHandle)!=0);119}120};121122#if STDMUTEX_RECURSION_CHECKS123struct _OwnerThread124{125// If this is to be read before locking, then the owner-thread variable must126// be atomic to prevent a torn read from spuriously causing errors.127std::atomic<DWORD> mOwnerThread;128constexpr _OwnerThread () noexcept : mOwnerThread(0) {}129static void on_deadlock (void)130{131using namespace std;132fprintf(stderr, "FATAL: Recursive locking of non-recursive mutex\133detected. Throwing system exception\n");134fflush(stderr);135__builtin_trap();136}137DWORD checkOwnerBeforeLock() const138{139DWORD self = GetCurrentThreadId();140if (mOwnerThread.load(std::memory_order_relaxed) == self)141on_deadlock();142return self;143}144void setOwnerAfterLock(DWORD id)145{146mOwnerThread.store(id, std::memory_order_relaxed);147}148void checkSetOwnerBeforeUnlock()149{150DWORD self = GetCurrentThreadId();151if (mOwnerThread.load(std::memory_order_relaxed) != self)152on_deadlock();153mOwnerThread.store(0, std::memory_order_relaxed);154}155};156#endif157158// Though the Slim Reader-Writer (SRW) locks used here are not complete until159// Windows 7, implementing partial functionality in Vista will simplify the160// interaction with condition variables.161162//Define SRWLOCK_INIT.163164#if !defined(SRWLOCK_INIT)165#pragma message "SRWLOCK_INIT macro is not defined. Defining automatically."166#define SRWLOCK_INIT {0}167#endif168169#if defined(_WIN32) && (WINVER >= _WIN32_WINNT_VISTA)170namespace windows7171{172class mutex173{174SRWLOCK mHandle;175// Track locking thread for error checking.176#if STDMUTEX_RECURSION_CHECKS177friend class vista::condition_variable;178_OwnerThread mOwnerThread {};179#endif180public:181typedef PSRWLOCK native_handle_type;182#pragma GCC diagnostic push183#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"184constexpr mutex () noexcept : mHandle(SRWLOCK_INIT) { }185#pragma GCC diagnostic pop186mutex (const mutex&) = delete;187mutex & operator= (const mutex&) = delete;188void lock (void)189{190// Note: Undefined behavior if called recursively.191#if STDMUTEX_RECURSION_CHECKS192DWORD self = mOwnerThread.checkOwnerBeforeLock();193#endif194AcquireSRWLockExclusive(&mHandle);195#if STDMUTEX_RECURSION_CHECKS196mOwnerThread.setOwnerAfterLock(self);197#endif198}199void unlock (void)200{201#if STDMUTEX_RECURSION_CHECKS202mOwnerThread.checkSetOwnerBeforeUnlock();203#endif204ReleaseSRWLockExclusive(&mHandle);205}206// TryAcquireSRW functions are a Windows 7 feature.207#if (WINVER >= _WIN32_WINNT_WIN7)208bool try_lock (void)209{210#if STDMUTEX_RECURSION_CHECKS211DWORD self = mOwnerThread.checkOwnerBeforeLock();212#endif213BOOL ret = TryAcquireSRWLockExclusive(&mHandle);214#if STDMUTEX_RECURSION_CHECKS215if (ret)216mOwnerThread.setOwnerAfterLock(self);217#endif218return ret;219}220#endif221native_handle_type native_handle (void)222{223return &mHandle;224}225};226} // Namespace windows7227#endif // Compiling for Vista228namespace xp229{230class mutex231{232CRITICAL_SECTION mHandle;233std::atomic_uchar mState;234// Track locking thread for error checking.235#if STDMUTEX_RECURSION_CHECKS236friend class vista::condition_variable;237_OwnerThread mOwnerThread {};238#endif239public:240typedef PCRITICAL_SECTION native_handle_type;241constexpr mutex () noexcept : mHandle(), mState(2) { }242mutex (const mutex&) = delete;243mutex & operator= (const mutex&) = delete;244~mutex() noexcept245{246// Undefined behavior if the mutex is held (locked) by any thread.247// Undefined behavior if a thread terminates while holding ownership of the248// mutex.249DeleteCriticalSection(&mHandle);250}251void lock (void)252{253unsigned char state = mState.load(std::memory_order_acquire);254while (state) {255if ((state == 2) && mState.compare_exchange_weak(state, 1, std::memory_order_acquire))256{257InitializeCriticalSection(&mHandle);258mState.store(0, std::memory_order_release);259break;260}261if (state == 1)262{263Sleep(0);264state = mState.load(std::memory_order_acquire);265}266}267#if STDMUTEX_RECURSION_CHECKS268DWORD self = mOwnerThread.checkOwnerBeforeLock();269#endif270EnterCriticalSection(&mHandle);271#if STDMUTEX_RECURSION_CHECKS272mOwnerThread.setOwnerAfterLock(self);273#endif274}275void unlock (void)276{277#if STDMUTEX_RECURSION_CHECKS278mOwnerThread.checkSetOwnerBeforeUnlock();279#endif280LeaveCriticalSection(&mHandle);281}282bool try_lock (void)283{284unsigned char state = mState.load(std::memory_order_acquire);285if ((state == 2) && mState.compare_exchange_strong(state, 1, std::memory_order_acquire))286{287InitializeCriticalSection(&mHandle);288mState.store(0, std::memory_order_release);289}290if (state == 1)291return false;292#if STDMUTEX_RECURSION_CHECKS293DWORD self = mOwnerThread.checkOwnerBeforeLock();294#endif295BOOL ret = TryEnterCriticalSection(&mHandle);296#if STDMUTEX_RECURSION_CHECKS297if (ret)298mOwnerThread.setOwnerAfterLock(self);299#endif300return ret;301}302native_handle_type native_handle (void)303{304return &mHandle;305}306};307} // Namespace "xp"308#if (WINVER >= _WIN32_WINNT_WIN7)309using windows7::mutex;310#else311using xp::mutex;312#endif313314class recursive_timed_mutex315{316static constexpr DWORD kWaitAbandoned = 0x00000080l;317static constexpr DWORD kWaitObject0 = 0x00000000l;318static constexpr DWORD kInfinite = 0xffffffffl;319inline bool try_lock_internal (DWORD ms) noexcept320{321DWORD ret = WaitForSingleObject(mHandle, ms);322#ifndef NDEBUG323if (ret == kWaitAbandoned)324{325using namespace std;326fprintf(stderr, "FATAL: Thread terminated while holding a mutex.");327terminate();328}329#endif330return (ret == kWaitObject0) || (ret == kWaitAbandoned);331}332protected:333HANDLE mHandle;334// Track locking thread for error checking of non-recursive timed_mutex. For335// standard compliance, this must be defined in same class and at the same336// access-control level as every other variable in the timed_mutex.337#if STDMUTEX_RECURSION_CHECKS338friend class vista::condition_variable;339_OwnerThread mOwnerThread {};340#endif341public:342typedef HANDLE native_handle_type;343native_handle_type native_handle() const {return mHandle;}344recursive_timed_mutex(const recursive_timed_mutex&) = delete;345recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete;346recursive_timed_mutex(): mHandle(CreateMutex(NULL, FALSE, NULL)) {}347~recursive_timed_mutex()348{349CloseHandle(mHandle);350}351void lock()352{353DWORD ret = WaitForSingleObject(mHandle, kInfinite);354// If (ret == WAIT_ABANDONED), then the thread that held ownership was355// terminated. Behavior is undefined, but Windows will pass ownership to this356// thread.357#ifndef NDEBUG358if (ret == kWaitAbandoned)359{360using namespace std;361fprintf(stderr, "FATAL: Thread terminated while holding a mutex.");362terminate();363}364#endif365if ((ret != kWaitObject0) && (ret != kWaitAbandoned))366{367__builtin_trap();368}369}370void unlock()371{372if (!ReleaseMutex(mHandle))373__builtin_trap();374}375bool try_lock()376{377return try_lock_internal(0);378}379template <class Rep, class Period>380bool try_lock_for(const std::chrono::duration<Rep,Period>& dur)381{382using namespace std::chrono;383auto timeout = duration_cast<milliseconds>(dur).count();384while (timeout > 0)385{386constexpr auto kMaxStep = static_cast<decltype(timeout)>(kInfinite-1);387auto step = (timeout < kMaxStep) ? timeout : kMaxStep;388if (try_lock_internal(static_cast<DWORD>(step)))389return true;390timeout -= step;391}392return false;393}394template <class Clock, class Duration>395bool try_lock_until(const std::chrono::time_point<Clock,Duration>& timeout_time)396{397return try_lock_for(timeout_time - Clock::now());398}399};400401// Override if, and only if, it is necessary for error-checking.402#if STDMUTEX_RECURSION_CHECKS403class timed_mutex: recursive_timed_mutex404{405public:406timed_mutex() = default;407timed_mutex(const timed_mutex&) = delete;408timed_mutex& operator=(const timed_mutex&) = delete;409void lock()410{411DWORD self = mOwnerThread.checkOwnerBeforeLock();412recursive_timed_mutex::lock();413mOwnerThread.setOwnerAfterLock(self);414}415void unlock()416{417mOwnerThread.checkSetOwnerBeforeUnlock();418recursive_timed_mutex::unlock();419}420template <class Rep, class Period>421bool try_lock_for(const std::chrono::duration<Rep,Period>& dur)422{423DWORD self = mOwnerThread.checkOwnerBeforeLock();424bool ret = recursive_timed_mutex::try_lock_for(dur);425if (ret)426mOwnerThread.setOwnerAfterLock(self);427return ret;428}429template <class Clock, class Duration>430bool try_lock_until(const std::chrono::time_point<Clock,Duration>& timeout_time)431{432return try_lock_for(timeout_time - Clock::now());433}434bool try_lock ()435{436return try_lock_for(std::chrono::milliseconds(0));437}438};439#else440typedef recursive_timed_mutex timed_mutex;441#endif442443class once_flag444{445// When available, the SRW-based mutexes should be faster than the446// CriticalSection-based mutexes. Only try_lock will be unavailable in Vista,447// and try_lock is not used by once_flag.448#if (_WIN32_WINNT == _WIN32_WINNT_VISTA)449windows7::mutex mMutex;450#else451mutex mMutex;452#endif453std::atomic_bool mHasRun;454once_flag(const once_flag&) = delete;455once_flag& operator=(const once_flag&) = delete;456template<class Callable, class... Args>457friend void call_once(once_flag& once, Callable&& f, Args&&... args);458public:459constexpr once_flag() noexcept: mMutex(), mHasRun(false) {}460};461462template<class Callable, class... Args>463void call_once(once_flag& flag, Callable&& func, Args&&... args)464{465if (flag.mHasRun.load(std::memory_order_acquire))466return;467lock_guard<decltype(flag.mMutex)> lock(flag.mMutex);468if (flag.mHasRun.load(std::memory_order_relaxed))469return;470detail::invoke(std::forward<Callable>(func),std::forward<Args>(args)...);471flag.mHasRun.store(true, std::memory_order_release);472}473} // Namespace mingw_stdthread474475// Push objects into std, but only if they are not already there.476namespace std477{478// Because of quirks of the compiler, the common "using namespace std;"479// directive would flatten the namespaces and introduce ambiguity where there480// was none. Direct specification (std::), however, would be unaffected.481// Take the safe option, and include only in the presence of MinGW's win32482// implementation.483#if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS) && !defined(__clang__)484using mingw_stdthread::recursive_mutex;485using mingw_stdthread::mutex;486using mingw_stdthread::recursive_timed_mutex;487using mingw_stdthread::timed_mutex;488using mingw_stdthread::once_flag;489using mingw_stdthread::call_once;490#elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING) // Skip repetition491#define MINGW_STDTHREAD_REDUNDANCY_WARNING492#pragma message "This version of MinGW seems to include a win32 port of\493pthreads, and probably already has C++11 std threading classes implemented,\494based on pthreads. These classes, found in namespace std, are not overridden\495by the mingw-std-thread library. If you would still like to use this\496implementation (as it is more lightweight), use the classes provided in\497namespace mingw_stdthread."498#endif499}500#endif // WIN32STDMUTEX_H501502503