Path: blob/master/thirdparty/mingw-std-threads/mingw.condition_variable.h
21097 views
/**1* @file condition_variable.h2* @brief std::condition_variable implementation for MinGW3*4* (c) 2013-2016 by Mega Limited, Auckland, New Zealand5* @author Alexander Vassilev6*7* @copyright Simplified (2-clause) BSD License.8* You should have received a copy of the license along with this9* 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 of13* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.14* @note15* 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*/1920#ifndef MINGW_CONDITIONAL_VARIABLE_H21#define MINGW_CONDITIONAL_VARIABLE_H2223#if !defined(__cplusplus) || (__cplusplus < 201103L)24#error A C++11 compiler is required!25#endif26// Use the standard classes for std::, if available.27#include <condition_variable>2829#include <cassert>30#include <chrono>31#include <exception>32#include <system_error>3334#include <sdkddkver.h> // Detect Windows version.35#if (WINVER < _WIN32_WINNT_VISTA)36#include <atomic>37#endif38#if (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))39#pragma message "The Windows API that MinGW-w32 provides is not fully compatible\40with Microsoft's API. We'll try to work around this, but we can make no\41guarantees. This problem does not exist in MinGW-w64."42#include <windows.h> // No further granularity can be expected.43#else44#if (WINVER < _WIN32_WINNT_VISTA)45#include <windef.h>46#include <winbase.h> // For CreateSemaphore47#include <handleapi.h>48#endif49#include <synchapi.h>50#endif5152#include "mingw.mutex.h"53#include "mingw.shared_mutex.h"5455#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#endif5859namespace mingw_stdthread60{61#if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS) && !defined(__clang__)62enum class cv_status { no_timeout, timeout };63#else64using std::cv_status;65#endif66namespace xp67{68// Include the XP-compatible condition_variable classes only if actually69// compiling for XP. The XP-compatible classes are slower than the newer70// versions, and depend on features not compatible with Windows Phone 8.71#if (WINVER < _WIN32_WINNT_VISTA)72class condition_variable_any73{74recursive_mutex mMutex {};75std::atomic<int> mNumWaiters {0};76HANDLE mSemaphore;77HANDLE mWakeEvent {};78public:79using native_handle_type = HANDLE;80native_handle_type native_handle()81{82return mSemaphore;83}84condition_variable_any(const condition_variable_any&) = delete;85condition_variable_any& operator=(const condition_variable_any&) = delete;86condition_variable_any()87: mSemaphore(CreateSemaphoreA(NULL, 0, 0xFFFF, NULL))88{89if (mSemaphore == NULL)90__builtin_trap();91mWakeEvent = CreateEvent(NULL, FALSE, FALSE, NULL);92if (mWakeEvent == NULL)93{94CloseHandle(mSemaphore);95__builtin_trap();96}97}98~condition_variable_any()99{100CloseHandle(mWakeEvent);101CloseHandle(mSemaphore);102}103private:104template <class M>105bool wait_impl(M& lock, DWORD timeout)106{107{108lock_guard<recursive_mutex> guard(mMutex);109mNumWaiters++;110}111lock.unlock();112DWORD ret = WaitForSingleObject(mSemaphore, timeout);113114mNumWaiters--;115SetEvent(mWakeEvent);116lock.lock();117if (ret == WAIT_OBJECT_0)118return true;119else if (ret == WAIT_TIMEOUT)120return false;121//2 possible cases:122//1)The point in notify_all() where we determine the count to123//increment the semaphore with has not been reached yet:124//we just need to decrement mNumWaiters, but setting the event does not hurt125//126//2)Semaphore has just been released with mNumWaiters just before127//we decremented it. This means that the semaphore count128//after all waiters finish won't be 0 - because not all waiters129//woke up by acquiring the semaphore - we woke up by a timeout.130//The notify_all() must handle this gracefully131//132else133{134using namespace std;135__builtin_trap();136}137}138public:139template <class M>140void wait(M& lock)141{142wait_impl(lock, INFINITE);143}144template <class M, class Predicate>145void wait(M& lock, Predicate pred)146{147while(!pred())148{149wait(lock);150};151}152153void notify_all() noexcept154{155lock_guard<recursive_mutex> lock(mMutex); //block any further wait requests until all current waiters are unblocked156if (mNumWaiters.load() <= 0)157return;158159ReleaseSemaphore(mSemaphore, mNumWaiters, NULL);160while(mNumWaiters > 0)161{162auto ret = WaitForSingleObject(mWakeEvent, 1000);163if (ret == WAIT_FAILED || ret == WAIT_ABANDONED)164std::terminate();165}166assert(mNumWaiters == 0);167//in case some of the waiters timed out just after we released the168//semaphore by mNumWaiters, it won't be zero now, because not all waiters169//woke up by acquiring the semaphore. So we must zero the semaphore before170//we accept waiters for the next event171//See _wait_impl for details172while(WaitForSingleObject(mSemaphore, 0) == WAIT_OBJECT_0);173}174void notify_one() noexcept175{176lock_guard<recursive_mutex> lock(mMutex);177int targetWaiters = mNumWaiters.load() - 1;178if (targetWaiters <= -1)179return;180ReleaseSemaphore(mSemaphore, 1, NULL);181while(mNumWaiters > targetWaiters)182{183auto ret = WaitForSingleObject(mWakeEvent, 1000);184if (ret == WAIT_FAILED || ret == WAIT_ABANDONED)185std::terminate();186}187assert(mNumWaiters == targetWaiters);188}189template <class M, class Rep, class Period>190cv_status wait_for(M& lock,191const std::chrono::duration<Rep, Period>& rel_time)192{193using namespace std::chrono;194auto timeout = duration_cast<milliseconds>(rel_time).count();195DWORD waittime = (timeout < INFINITE) ? ((timeout < 0) ? 0 : static_cast<DWORD>(timeout)) : (INFINITE - 1);196bool ret = wait_impl(lock, waittime) || (timeout >= INFINITE);197return ret?cv_status::no_timeout:cv_status::timeout;198}199200template <class M, class Rep, class Period, class Predicate>201bool wait_for(M& lock,202const std::chrono::duration<Rep, Period>& rel_time, Predicate pred)203{204return wait_until(lock, std::chrono::steady_clock::now()+rel_time, pred);205}206template <class M, class Clock, class Duration>207cv_status wait_until (M& lock,208const std::chrono::time_point<Clock,Duration>& abs_time)209{210return wait_for(lock, abs_time - Clock::now());211}212template <class M, class Clock, class Duration, class Predicate>213bool wait_until (M& lock,214const std::chrono::time_point<Clock, Duration>& abs_time,215Predicate pred)216{217while (!pred())218{219if (wait_until(lock, abs_time) == cv_status::timeout)220{221return pred();222}223}224return true;225}226};227class condition_variable: condition_variable_any228{229using base = condition_variable_any;230public:231using base::native_handle_type;232using base::native_handle;233using base::base;234using base::notify_all;235using base::notify_one;236void wait(unique_lock<mutex> &lock)237{238base::wait(lock);239}240template <class Predicate>241void wait(unique_lock<mutex>& lock, Predicate pred)242{243base::wait(lock, pred);244}245template <class Rep, class Period>246cv_status wait_for(unique_lock<mutex>& lock, const std::chrono::duration<Rep, Period>& rel_time)247{248return base::wait_for(lock, rel_time);249}250template <class Rep, class Period, class Predicate>251bool wait_for(unique_lock<mutex>& lock, const std::chrono::duration<Rep, Period>& rel_time, Predicate pred)252{253return base::wait_for(lock, rel_time, pred);254}255template <class Clock, class Duration>256cv_status wait_until (unique_lock<mutex>& lock, const std::chrono::time_point<Clock,Duration>& abs_time)257{258return base::wait_until(lock, abs_time);259}260template <class Clock, class Duration, class Predicate>261bool wait_until (unique_lock<mutex>& lock, const std::chrono::time_point<Clock, Duration>& abs_time, Predicate pred)262{263return base::wait_until(lock, abs_time, pred);264}265};266#endif // Compiling for XP267} // Namespace mingw_stdthread::xp268269#if (WINVER >= _WIN32_WINNT_VISTA)270namespace vista271{272// If compiling for Vista or higher, use the native condition variable.273class condition_variable274{275static constexpr DWORD kInfinite = 0xffffffffl;276#pragma GCC diagnostic push277#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"278CONDITION_VARIABLE cvariable_ = CONDITION_VARIABLE_INIT;279#pragma GCC diagnostic pop280281friend class condition_variable_any;282283#if STDMUTEX_RECURSION_CHECKS284template<typename MTX>285inline static void before_wait (MTX * pmutex)286{287pmutex->mOwnerThread.checkSetOwnerBeforeUnlock();288}289template<typename MTX>290inline static void after_wait (MTX * pmutex)291{292pmutex->mOwnerThread.setOwnerAfterLock(GetCurrentThreadId());293}294#else295inline static void before_wait (void *) { }296inline static void after_wait (void *) { }297#endif298299bool wait_impl (unique_lock<xp::mutex> & lock, DWORD time)300{301using mutex_handle_type = typename xp::mutex::native_handle_type;302static_assert(std::is_same<mutex_handle_type, PCRITICAL_SECTION>::value,303"Native Win32 condition variable requires std::mutex to \304use native Win32 critical section objects.");305xp::mutex * pmutex = lock.release();306before_wait(pmutex);307BOOL success = SleepConditionVariableCS(&cvariable_,308pmutex->native_handle(),309time);310after_wait(pmutex);311lock = unique_lock<xp::mutex>(*pmutex, adopt_lock);312return success;313}314315bool wait_unique (windows7::mutex * pmutex, DWORD time)316{317before_wait(pmutex);318BOOL success = SleepConditionVariableSRW( native_handle(),319pmutex->native_handle(),320time,321// CONDITION_VARIABLE_LOCKMODE_SHARED has a value not specified by322// Microsoft's Dev Center, but is known to be (convertible to) a ULONG. To323// ensure that the value passed to this function is not equal to Microsoft's324// constant, we can either use a static_assert, or simply generate an325// appropriate value.326!CONDITION_VARIABLE_LOCKMODE_SHARED);327after_wait(pmutex);328return success;329}330bool wait_impl (unique_lock<windows7::mutex> & lock, DWORD time)331{332windows7::mutex * pmutex = lock.release();333bool success = wait_unique(pmutex, time);334lock = unique_lock<windows7::mutex>(*pmutex, adopt_lock);335return success;336}337public:338using native_handle_type = PCONDITION_VARIABLE;339native_handle_type native_handle (void)340{341return &cvariable_;342}343344condition_variable (void) = default;345~condition_variable (void) = default;346347condition_variable (const condition_variable &) = delete;348condition_variable & operator= (const condition_variable &) = delete;349350void notify_one (void) noexcept351{352WakeConditionVariable(&cvariable_);353}354355void notify_all (void) noexcept356{357WakeAllConditionVariable(&cvariable_);358}359360void wait (unique_lock<mutex> & lock)361{362wait_impl(lock, kInfinite);363}364365template<class Predicate>366void wait (unique_lock<mutex> & lock, Predicate pred)367{368while (!pred())369wait(lock);370}371372template <class Rep, class Period>373cv_status wait_for(unique_lock<mutex>& lock,374const std::chrono::duration<Rep, Period>& rel_time)375{376using namespace std::chrono;377auto timeout = duration_cast<milliseconds>(rel_time).count();378DWORD waittime = (timeout < kInfinite) ? ((timeout < 0) ? 0 : static_cast<DWORD>(timeout)) : (kInfinite - 1);379bool result = wait_impl(lock, waittime) || (timeout >= kInfinite);380return result ? cv_status::no_timeout : cv_status::timeout;381}382383template <class Rep, class Period, class Predicate>384bool wait_for(unique_lock<mutex>& lock,385const std::chrono::duration<Rep, Period>& rel_time,386Predicate pred)387{388return wait_until(lock,389std::chrono::steady_clock::now() + rel_time,390std::move(pred));391}392template <class Clock, class Duration>393cv_status wait_until (unique_lock<mutex>& lock,394const std::chrono::time_point<Clock,Duration>& abs_time)395{396return wait_for(lock, abs_time - Clock::now());397}398template <class Clock, class Duration, class Predicate>399bool wait_until (unique_lock<mutex>& lock,400const std::chrono::time_point<Clock, Duration>& abs_time,401Predicate pred)402{403while (!pred())404{405if (wait_until(lock, abs_time) == cv_status::timeout)406{407return pred();408}409}410return true;411}412};413414class condition_variable_any415{416static constexpr DWORD kInfinite = 0xffffffffl;417using native_shared_mutex = windows7::shared_mutex;418419condition_variable internal_cv_ {};420// When available, the SRW-based mutexes should be faster than the421// CriticalSection-based mutexes. Only try_lock will be unavailable in Vista,422// and try_lock is not used by condition_variable_any.423windows7::mutex internal_mutex_ {};424425template<class L>426bool wait_impl (L & lock, DWORD time)427{428unique_lock<decltype(internal_mutex_)> internal_lock(internal_mutex_);429lock.unlock();430bool success = internal_cv_.wait_impl(internal_lock, time);431lock.lock();432return success;433}434// If the lock happens to be called on a native Windows mutex, skip any extra435// contention.436inline bool wait_impl (unique_lock<mutex> & lock, DWORD time)437{438return internal_cv_.wait_impl(lock, time);439}440// Some shared_mutex functionality is available even in Vista, but it's not441// until Windows 7 that a full implementation is natively possible. The class442// itself is defined, with missing features, at the Vista feature level.443bool wait_impl (unique_lock<native_shared_mutex> & lock, DWORD time)444{445native_shared_mutex * pmutex = lock.release();446bool success = internal_cv_.wait_unique(pmutex, time);447lock = unique_lock<native_shared_mutex>(*pmutex, adopt_lock);448return success;449}450bool wait_impl (shared_lock<native_shared_mutex> & lock, DWORD time)451{452native_shared_mutex * pmutex = lock.release();453BOOL success = SleepConditionVariableSRW(native_handle(),454pmutex->native_handle(), time,455CONDITION_VARIABLE_LOCKMODE_SHARED);456lock = shared_lock<native_shared_mutex>(*pmutex, adopt_lock);457return success;458}459public:460using native_handle_type = typename condition_variable::native_handle_type;461462native_handle_type native_handle (void)463{464return internal_cv_.native_handle();465}466467void notify_one (void) noexcept468{469internal_cv_.notify_one();470}471472void notify_all (void) noexcept473{474internal_cv_.notify_all();475}476477condition_variable_any (void) = default;478~condition_variable_any (void) = default;479480template<class L>481void wait (L & lock)482{483wait_impl(lock, kInfinite);484}485486template<class L, class Predicate>487void wait (L & lock, Predicate pred)488{489while (!pred())490wait(lock);491}492493template <class L, class Rep, class Period>494cv_status wait_for(L& lock, const std::chrono::duration<Rep,Period>& period)495{496using namespace std::chrono;497auto timeout = duration_cast<milliseconds>(period).count();498DWORD waittime = (timeout < kInfinite) ? ((timeout < 0) ? 0 : static_cast<DWORD>(timeout)) : (kInfinite - 1);499bool result = wait_impl(lock, waittime) || (timeout >= kInfinite);500return result ? cv_status::no_timeout : cv_status::timeout;501}502503template <class L, class Rep, class Period, class Predicate>504bool wait_for(L& lock, const std::chrono::duration<Rep, Period>& period,505Predicate pred)506{507return wait_until(lock, std::chrono::steady_clock::now() + period,508std::move(pred));509}510template <class L, class Clock, class Duration>511cv_status wait_until (L& lock,512const std::chrono::time_point<Clock,Duration>& abs_time)513{514return wait_for(lock, abs_time - Clock::now());515}516template <class L, class Clock, class Duration, class Predicate>517bool wait_until (L& lock,518const std::chrono::time_point<Clock, Duration>& abs_time,519Predicate pred)520{521while (!pred())522{523if (wait_until(lock, abs_time) == cv_status::timeout)524{525return pred();526}527}528return true;529}530};531} // Namespace vista532#endif533#if WINVER < 0x0600534using xp::condition_variable;535using xp::condition_variable_any;536#else537using vista::condition_variable;538using vista::condition_variable_any;539#endif540} // Namespace mingw_stdthread541542// Push objects into std, but only if they are not already there.543namespace std544{545// Because of quirks of the compiler, the common "using namespace std;"546// directive would flatten the namespaces and introduce ambiguity where there547// was none. Direct specification (std::), however, would be unaffected.548// Take the safe option, and include only in the presence of MinGW's win32549// implementation.550#if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS) && !defined(__clang__)551using mingw_stdthread::cv_status;552using mingw_stdthread::condition_variable;553using mingw_stdthread::condition_variable_any;554#elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING) // Skip repetition555#define MINGW_STDTHREAD_REDUNDANCY_WARNING556#pragma message "This version of MinGW seems to include a win32 port of\557pthreads, and probably already has C++11 std threading classes implemented,\558based on pthreads. These classes, found in namespace std, are not overridden\559by the mingw-std-thread library. If you would still like to use this\560implementation (as it is more lightweight), use the classes provided in\561namespace mingw_stdthread."562#endif563}564#endif // MINGW_CONDITIONAL_VARIABLE_H565566567