Path: blob/master/thirdparty/mingw-std-threads/mingw.mutex.h
9896 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 <mutex> //need for call_once()4041#if STDMUTEX_RECURSION_CHECKS || !defined(NDEBUG)42#include <cstdio>43#endif4445#include <sdkddkver.h> // Detect Windows version.4647#if (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))48#pragma message "The Windows API that MinGW-w32 provides is not fully compatible\49with Microsoft's API. We'll try to work around this, but we can make no\50guarantees. This problem does not exist in MinGW-w64."51#include <windows.h> // No further granularity can be expected.52#else53#if STDMUTEX_RECURSION_CHECKS54#include <processthreadsapi.h> // For GetCurrentThreadId55#endif56#include <synchapi.h> // For InitializeCriticalSection, etc.57#include <errhandlingapi.h> // For GetLastError58#include <handleapi.h>59#endif6061// Need for the implementation of invoke62#include "mingw.invoke.h"6364#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0501)65#error To use the MinGW-std-threads library, you will need to define the macro _WIN32_WINNT to be 0x0501 (Windows XP) or higher.66#endif6768namespace mingw_stdthread69{70// The _NonRecursive class has mechanisms that do not play nice with direct71// manipulation of the native handle. This forward declaration is part of72// a friend class declaration.73#if STDMUTEX_RECURSION_CHECKS74namespace vista75{76class condition_variable;77}78#endif79// To make this namespace equivalent to the thread-related subset of std,80// pull in the classes and class templates supplied by std but not by this81// implementation.82using std::lock_guard;83using std::unique_lock;84using std::adopt_lock_t;85using std::defer_lock_t;86using std::try_to_lock_t;87using std::adopt_lock;88using std::defer_lock;89using std::try_to_lock;9091class recursive_mutex92{93CRITICAL_SECTION mHandle;94public:95typedef LPCRITICAL_SECTION native_handle_type;96native_handle_type native_handle() {return &mHandle;}97recursive_mutex() noexcept : mHandle()98{99InitializeCriticalSection(&mHandle);100}101recursive_mutex (const recursive_mutex&) = delete;102recursive_mutex& operator=(const recursive_mutex&) = delete;103~recursive_mutex() noexcept104{105DeleteCriticalSection(&mHandle);106}107void lock()108{109EnterCriticalSection(&mHandle);110}111void unlock()112{113LeaveCriticalSection(&mHandle);114}115bool try_lock()116{117return (TryEnterCriticalSection(&mHandle)!=0);118}119};120121#if STDMUTEX_RECURSION_CHECKS122struct _OwnerThread123{124// If this is to be read before locking, then the owner-thread variable must125// be atomic to prevent a torn read from spuriously causing errors.126std::atomic<DWORD> mOwnerThread;127constexpr _OwnerThread () noexcept : mOwnerThread(0) {}128static void on_deadlock (void)129{130using namespace std;131fprintf(stderr, "FATAL: Recursive locking of non-recursive mutex\132detected. Throwing system exception\n");133fflush(stderr);134__builtin_trap();135}136DWORD checkOwnerBeforeLock() const137{138DWORD self = GetCurrentThreadId();139if (mOwnerThread.load(std::memory_order_relaxed) == self)140on_deadlock();141return self;142}143void setOwnerAfterLock(DWORD id)144{145mOwnerThread.store(id, std::memory_order_relaxed);146}147void checkSetOwnerBeforeUnlock()148{149DWORD self = GetCurrentThreadId();150if (mOwnerThread.load(std::memory_order_relaxed) != self)151on_deadlock();152mOwnerThread.store(0, std::memory_order_relaxed);153}154};155#endif156157// Though the Slim Reader-Writer (SRW) locks used here are not complete until158// Windows 7, implementing partial functionality in Vista will simplify the159// interaction with condition variables.160161//Define SRWLOCK_INIT.162163#if !defined(SRWLOCK_INIT)164#pragma message "SRWLOCK_INIT macro is not defined. Defining automatically."165#define SRWLOCK_INIT {0}166#endif167168#if defined(_WIN32) && (WINVER >= _WIN32_WINNT_VISTA)169namespace windows7170{171class mutex172{173SRWLOCK mHandle;174// Track locking thread for error checking.175#if STDMUTEX_RECURSION_CHECKS176friend class vista::condition_variable;177_OwnerThread mOwnerThread {};178#endif179public:180typedef PSRWLOCK native_handle_type;181#pragma GCC diagnostic push182#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"183constexpr mutex () noexcept : mHandle(SRWLOCK_INIT) { }184#pragma GCC diagnostic pop185mutex (const mutex&) = delete;186mutex & operator= (const mutex&) = delete;187void lock (void)188{189// Note: Undefined behavior if called recursively.190#if STDMUTEX_RECURSION_CHECKS191DWORD self = mOwnerThread.checkOwnerBeforeLock();192#endif193AcquireSRWLockExclusive(&mHandle);194#if STDMUTEX_RECURSION_CHECKS195mOwnerThread.setOwnerAfterLock(self);196#endif197}198void unlock (void)199{200#if STDMUTEX_RECURSION_CHECKS201mOwnerThread.checkSetOwnerBeforeUnlock();202#endif203ReleaseSRWLockExclusive(&mHandle);204}205// TryAcquireSRW functions are a Windows 7 feature.206#if (WINVER >= _WIN32_WINNT_WIN7)207bool try_lock (void)208{209#if STDMUTEX_RECURSION_CHECKS210DWORD self = mOwnerThread.checkOwnerBeforeLock();211#endif212BOOL ret = TryAcquireSRWLockExclusive(&mHandle);213#if STDMUTEX_RECURSION_CHECKS214if (ret)215mOwnerThread.setOwnerAfterLock(self);216#endif217return ret;218}219#endif220native_handle_type native_handle (void)221{222return &mHandle;223}224};225} // Namespace windows7226#endif // Compiling for Vista227namespace xp228{229class mutex230{231CRITICAL_SECTION mHandle;232std::atomic_uchar mState;233// Track locking thread for error checking.234#if STDMUTEX_RECURSION_CHECKS235friend class vista::condition_variable;236_OwnerThread mOwnerThread {};237#endif238public:239typedef PCRITICAL_SECTION native_handle_type;240constexpr mutex () noexcept : mHandle(), mState(2) { }241mutex (const mutex&) = delete;242mutex & operator= (const mutex&) = delete;243~mutex() noexcept244{245// Undefined behavior if the mutex is held (locked) by any thread.246// Undefined behavior if a thread terminates while holding ownership of the247// mutex.248DeleteCriticalSection(&mHandle);249}250void lock (void)251{252unsigned char state = mState.load(std::memory_order_acquire);253while (state) {254if ((state == 2) && mState.compare_exchange_weak(state, 1, std::memory_order_acquire))255{256InitializeCriticalSection(&mHandle);257mState.store(0, std::memory_order_release);258break;259}260if (state == 1)261{262Sleep(0);263state = mState.load(std::memory_order_acquire);264}265}266#if STDMUTEX_RECURSION_CHECKS267DWORD self = mOwnerThread.checkOwnerBeforeLock();268#endif269EnterCriticalSection(&mHandle);270#if STDMUTEX_RECURSION_CHECKS271mOwnerThread.setOwnerAfterLock(self);272#endif273}274void unlock (void)275{276#if STDMUTEX_RECURSION_CHECKS277mOwnerThread.checkSetOwnerBeforeUnlock();278#endif279LeaveCriticalSection(&mHandle);280}281bool try_lock (void)282{283unsigned char state = mState.load(std::memory_order_acquire);284if ((state == 2) && mState.compare_exchange_strong(state, 1, std::memory_order_acquire))285{286InitializeCriticalSection(&mHandle);287mState.store(0, std::memory_order_release);288}289if (state == 1)290return false;291#if STDMUTEX_RECURSION_CHECKS292DWORD self = mOwnerThread.checkOwnerBeforeLock();293#endif294BOOL ret = TryEnterCriticalSection(&mHandle);295#if STDMUTEX_RECURSION_CHECKS296if (ret)297mOwnerThread.setOwnerAfterLock(self);298#endif299return ret;300}301native_handle_type native_handle (void)302{303return &mHandle;304}305};306} // Namespace "xp"307#if (WINVER >= _WIN32_WINNT_WIN7)308using windows7::mutex;309#else310using xp::mutex;311#endif312313class recursive_timed_mutex314{315static constexpr DWORD kWaitAbandoned = 0x00000080l;316static constexpr DWORD kWaitObject0 = 0x00000000l;317static constexpr DWORD kInfinite = 0xffffffffl;318inline bool try_lock_internal (DWORD ms) noexcept319{320DWORD ret = WaitForSingleObject(mHandle, ms);321#ifndef NDEBUG322if (ret == kWaitAbandoned)323{324using namespace std;325fprintf(stderr, "FATAL: Thread terminated while holding a mutex.");326terminate();327}328#endif329return (ret == kWaitObject0) || (ret == kWaitAbandoned);330}331protected:332HANDLE mHandle;333// Track locking thread for error checking of non-recursive timed_mutex. For334// standard compliance, this must be defined in same class and at the same335// access-control level as every other variable in the timed_mutex.336#if STDMUTEX_RECURSION_CHECKS337friend class vista::condition_variable;338_OwnerThread mOwnerThread {};339#endif340public:341typedef HANDLE native_handle_type;342native_handle_type native_handle() const {return mHandle;}343recursive_timed_mutex(const recursive_timed_mutex&) = delete;344recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete;345recursive_timed_mutex(): mHandle(CreateMutex(NULL, FALSE, NULL)) {}346~recursive_timed_mutex()347{348CloseHandle(mHandle);349}350void lock()351{352DWORD ret = WaitForSingleObject(mHandle, kInfinite);353// If (ret == WAIT_ABANDONED), then the thread that held ownership was354// terminated. Behavior is undefined, but Windows will pass ownership to this355// thread.356#ifndef NDEBUG357if (ret == kWaitAbandoned)358{359using namespace std;360fprintf(stderr, "FATAL: Thread terminated while holding a mutex.");361terminate();362}363#endif364if ((ret != kWaitObject0) && (ret != kWaitAbandoned))365{366__builtin_trap();367}368}369void unlock()370{371if (!ReleaseMutex(mHandle))372__builtin_trap();373}374bool try_lock()375{376return try_lock_internal(0);377}378template <class Rep, class Period>379bool try_lock_for(const std::chrono::duration<Rep,Period>& dur)380{381using namespace std::chrono;382auto timeout = duration_cast<milliseconds>(dur).count();383while (timeout > 0)384{385constexpr auto kMaxStep = static_cast<decltype(timeout)>(kInfinite-1);386auto step = (timeout < kMaxStep) ? timeout : kMaxStep;387if (try_lock_internal(static_cast<DWORD>(step)))388return true;389timeout -= step;390}391return false;392}393template <class Clock, class Duration>394bool try_lock_until(const std::chrono::time_point<Clock,Duration>& timeout_time)395{396return try_lock_for(timeout_time - Clock::now());397}398};399400// Override if, and only if, it is necessary for error-checking.401#if STDMUTEX_RECURSION_CHECKS402class timed_mutex: recursive_timed_mutex403{404public:405timed_mutex() = default;406timed_mutex(const timed_mutex&) = delete;407timed_mutex& operator=(const timed_mutex&) = delete;408void lock()409{410DWORD self = mOwnerThread.checkOwnerBeforeLock();411recursive_timed_mutex::lock();412mOwnerThread.setOwnerAfterLock(self);413}414void unlock()415{416mOwnerThread.checkSetOwnerBeforeUnlock();417recursive_timed_mutex::unlock();418}419template <class Rep, class Period>420bool try_lock_for(const std::chrono::duration<Rep,Period>& dur)421{422DWORD self = mOwnerThread.checkOwnerBeforeLock();423bool ret = recursive_timed_mutex::try_lock_for(dur);424if (ret)425mOwnerThread.setOwnerAfterLock(self);426return ret;427}428template <class Clock, class Duration>429bool try_lock_until(const std::chrono::time_point<Clock,Duration>& timeout_time)430{431return try_lock_for(timeout_time - Clock::now());432}433bool try_lock ()434{435return try_lock_for(std::chrono::milliseconds(0));436}437};438#else439typedef recursive_timed_mutex timed_mutex;440#endif441442class once_flag443{444// When available, the SRW-based mutexes should be faster than the445// CriticalSection-based mutexes. Only try_lock will be unavailable in Vista,446// and try_lock is not used by once_flag.447#if (_WIN32_WINNT == _WIN32_WINNT_VISTA)448windows7::mutex mMutex;449#else450mutex mMutex;451#endif452std::atomic_bool mHasRun;453once_flag(const once_flag&) = delete;454once_flag& operator=(const once_flag&) = delete;455template<class Callable, class... Args>456friend void call_once(once_flag& once, Callable&& f, Args&&... args);457public:458constexpr once_flag() noexcept: mMutex(), mHasRun(false) {}459};460461template<class Callable, class... Args>462void call_once(once_flag& flag, Callable&& func, Args&&... args)463{464if (flag.mHasRun.load(std::memory_order_acquire))465return;466lock_guard<decltype(flag.mMutex)> lock(flag.mMutex);467if (flag.mHasRun.load(std::memory_order_relaxed))468return;469detail::invoke(std::forward<Callable>(func),std::forward<Args>(args)...);470flag.mHasRun.store(true, std::memory_order_release);471}472} // Namespace mingw_stdthread473474// Push objects into std, but only if they are not already there.475namespace std476{477// Because of quirks of the compiler, the common "using namespace std;"478// directive would flatten the namespaces and introduce ambiguity where there479// was none. Direct specification (std::), however, would be unaffected.480// Take the safe option, and include only in the presence of MinGW's win32481// implementation.482#if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS) && !defined(__clang__)483using mingw_stdthread::recursive_mutex;484using mingw_stdthread::mutex;485using mingw_stdthread::recursive_timed_mutex;486using mingw_stdthread::timed_mutex;487using mingw_stdthread::once_flag;488using mingw_stdthread::call_once;489#elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING) // Skip repetition490#define MINGW_STDTHREAD_REDUNDANCY_WARNING491#pragma message "This version of MinGW seems to include a win32 port of\492pthreads, and probably already has C++11 std threading classes implemented,\493based on pthreads. These classes, found in namespace std, are not overridden\494by the mingw-std-thread library. If you would still like to use this\495implementation (as it is more lightweight), use the classes provided in\496namespace mingw_stdthread."497#endif498}499#endif // WIN32STDMUTEX_H500501502