Path: blob/master/thirdparty/mingw-std-threads/mingw.condition_variable.h
9902 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 <system_error>3233#include <sdkddkver.h> // Detect Windows version.34#if (WINVER < _WIN32_WINNT_VISTA)35#include <atomic>36#endif37#if (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))38#pragma message "The Windows API that MinGW-w32 provides is not fully compatible\39with Microsoft's API. We'll try to work around this, but we can make no\40guarantees. This problem does not exist in MinGW-w64."41#include <windows.h> // No further granularity can be expected.42#else43#if (WINVER < _WIN32_WINNT_VISTA)44#include <windef.h>45#include <winbase.h> // For CreateSemaphore46#include <handleapi.h>47#endif48#include <synchapi.h>49#endif5051#include "mingw.mutex.h"52#include "mingw.shared_mutex.h"5354#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0501)55#error To use the MinGW-std-threads library, you will need to define the macro _WIN32_WINNT to be 0x0501 (Windows XP) or higher.56#endif5758namespace mingw_stdthread59{60#if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS) && !defined(__clang__)61enum class cv_status { no_timeout, timeout };62#else63using std::cv_status;64#endif65namespace xp66{67// Include the XP-compatible condition_variable classes only if actually68// compiling for XP. The XP-compatible classes are slower than the newer69// versions, and depend on features not compatible with Windows Phone 8.70#if (WINVER < _WIN32_WINNT_VISTA)71class condition_variable_any72{73recursive_mutex mMutex {};74std::atomic<int> mNumWaiters {0};75HANDLE mSemaphore;76HANDLE mWakeEvent {};77public:78using native_handle_type = HANDLE;79native_handle_type native_handle()80{81return mSemaphore;82}83condition_variable_any(const condition_variable_any&) = delete;84condition_variable_any& operator=(const condition_variable_any&) = delete;85condition_variable_any()86: mSemaphore(CreateSemaphoreA(NULL, 0, 0xFFFF, NULL))87{88if (mSemaphore == NULL)89__builtin_trap();90mWakeEvent = CreateEvent(NULL, FALSE, FALSE, NULL);91if (mWakeEvent == NULL)92{93CloseHandle(mSemaphore);94__builtin_trap();95}96}97~condition_variable_any()98{99CloseHandle(mWakeEvent);100CloseHandle(mSemaphore);101}102private:103template <class M>104bool wait_impl(M& lock, DWORD timeout)105{106{107lock_guard<recursive_mutex> guard(mMutex);108mNumWaiters++;109}110lock.unlock();111DWORD ret = WaitForSingleObject(mSemaphore, timeout);112113mNumWaiters--;114SetEvent(mWakeEvent);115lock.lock();116if (ret == WAIT_OBJECT_0)117return true;118else if (ret == WAIT_TIMEOUT)119return false;120//2 possible cases:121//1)The point in notify_all() where we determine the count to122//increment the semaphore with has not been reached yet:123//we just need to decrement mNumWaiters, but setting the event does not hurt124//125//2)Semaphore has just been released with mNumWaiters just before126//we decremented it. This means that the semaphore count127//after all waiters finish won't be 0 - because not all waiters128//woke up by acquiring the semaphore - we woke up by a timeout.129//The notify_all() must handle this gracefully130//131else132{133using namespace std;134__builtin_trap();135}136}137public:138template <class M>139void wait(M& lock)140{141wait_impl(lock, INFINITE);142}143template <class M, class Predicate>144void wait(M& lock, Predicate pred)145{146while(!pred())147{148wait(lock);149};150}151152void notify_all() noexcept153{154lock_guard<recursive_mutex> lock(mMutex); //block any further wait requests until all current waiters are unblocked155if (mNumWaiters.load() <= 0)156return;157158ReleaseSemaphore(mSemaphore, mNumWaiters, NULL);159while(mNumWaiters > 0)160{161auto ret = WaitForSingleObject(mWakeEvent, 1000);162if (ret == WAIT_FAILED || ret == WAIT_ABANDONED)163std::terminate();164}165assert(mNumWaiters == 0);166//in case some of the waiters timed out just after we released the167//semaphore by mNumWaiters, it won't be zero now, because not all waiters168//woke up by acquiring the semaphore. So we must zero the semaphore before169//we accept waiters for the next event170//See _wait_impl for details171while(WaitForSingleObject(mSemaphore, 0) == WAIT_OBJECT_0);172}173void notify_one() noexcept174{175lock_guard<recursive_mutex> lock(mMutex);176int targetWaiters = mNumWaiters.load() - 1;177if (targetWaiters <= -1)178return;179ReleaseSemaphore(mSemaphore, 1, NULL);180while(mNumWaiters > targetWaiters)181{182auto ret = WaitForSingleObject(mWakeEvent, 1000);183if (ret == WAIT_FAILED || ret == WAIT_ABANDONED)184std::terminate();185}186assert(mNumWaiters == targetWaiters);187}188template <class M, class Rep, class Period>189cv_status wait_for(M& lock,190const std::chrono::duration<Rep, Period>& rel_time)191{192using namespace std::chrono;193auto timeout = duration_cast<milliseconds>(rel_time).count();194DWORD waittime = (timeout < INFINITE) ? ((timeout < 0) ? 0 : static_cast<DWORD>(timeout)) : (INFINITE - 1);195bool ret = wait_impl(lock, waittime) || (timeout >= INFINITE);196return ret?cv_status::no_timeout:cv_status::timeout;197}198199template <class M, class Rep, class Period, class Predicate>200bool wait_for(M& lock,201const std::chrono::duration<Rep, Period>& rel_time, Predicate pred)202{203return wait_until(lock, std::chrono::steady_clock::now()+rel_time, pred);204}205template <class M, class Clock, class Duration>206cv_status wait_until (M& lock,207const std::chrono::time_point<Clock,Duration>& abs_time)208{209return wait_for(lock, abs_time - Clock::now());210}211template <class M, class Clock, class Duration, class Predicate>212bool wait_until (M& lock,213const std::chrono::time_point<Clock, Duration>& abs_time,214Predicate pred)215{216while (!pred())217{218if (wait_until(lock, abs_time) == cv_status::timeout)219{220return pred();221}222}223return true;224}225};226class condition_variable: condition_variable_any227{228using base = condition_variable_any;229public:230using base::native_handle_type;231using base::native_handle;232using base::base;233using base::notify_all;234using base::notify_one;235void wait(unique_lock<mutex> &lock)236{237base::wait(lock);238}239template <class Predicate>240void wait(unique_lock<mutex>& lock, Predicate pred)241{242base::wait(lock, pred);243}244template <class Rep, class Period>245cv_status wait_for(unique_lock<mutex>& lock, const std::chrono::duration<Rep, Period>& rel_time)246{247return base::wait_for(lock, rel_time);248}249template <class Rep, class Period, class Predicate>250bool wait_for(unique_lock<mutex>& lock, const std::chrono::duration<Rep, Period>& rel_time, Predicate pred)251{252return base::wait_for(lock, rel_time, pred);253}254template <class Clock, class Duration>255cv_status wait_until (unique_lock<mutex>& lock, const std::chrono::time_point<Clock,Duration>& abs_time)256{257return base::wait_until(lock, abs_time);258}259template <class Clock, class Duration, class Predicate>260bool wait_until (unique_lock<mutex>& lock, const std::chrono::time_point<Clock, Duration>& abs_time, Predicate pred)261{262return base::wait_until(lock, abs_time, pred);263}264};265#endif // Compiling for XP266} // Namespace mingw_stdthread::xp267268#if (WINVER >= _WIN32_WINNT_VISTA)269namespace vista270{271// If compiling for Vista or higher, use the native condition variable.272class condition_variable273{274static constexpr DWORD kInfinite = 0xffffffffl;275#pragma GCC diagnostic push276#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"277CONDITION_VARIABLE cvariable_ = CONDITION_VARIABLE_INIT;278#pragma GCC diagnostic pop279280friend class condition_variable_any;281282#if STDMUTEX_RECURSION_CHECKS283template<typename MTX>284inline static void before_wait (MTX * pmutex)285{286pmutex->mOwnerThread.checkSetOwnerBeforeUnlock();287}288template<typename MTX>289inline static void after_wait (MTX * pmutex)290{291pmutex->mOwnerThread.setOwnerAfterLock(GetCurrentThreadId());292}293#else294inline static void before_wait (void *) { }295inline static void after_wait (void *) { }296#endif297298bool wait_impl (unique_lock<xp::mutex> & lock, DWORD time)299{300using mutex_handle_type = typename xp::mutex::native_handle_type;301static_assert(std::is_same<mutex_handle_type, PCRITICAL_SECTION>::value,302"Native Win32 condition variable requires std::mutex to \303use native Win32 critical section objects.");304xp::mutex * pmutex = lock.release();305before_wait(pmutex);306BOOL success = SleepConditionVariableCS(&cvariable_,307pmutex->native_handle(),308time);309after_wait(pmutex);310lock = unique_lock<xp::mutex>(*pmutex, adopt_lock);311return success;312}313314bool wait_unique (windows7::mutex * pmutex, DWORD time)315{316before_wait(pmutex);317BOOL success = SleepConditionVariableSRW( native_handle(),318pmutex->native_handle(),319time,320// CONDITION_VARIABLE_LOCKMODE_SHARED has a value not specified by321// Microsoft's Dev Center, but is known to be (convertible to) a ULONG. To322// ensure that the value passed to this function is not equal to Microsoft's323// constant, we can either use a static_assert, or simply generate an324// appropriate value.325!CONDITION_VARIABLE_LOCKMODE_SHARED);326after_wait(pmutex);327return success;328}329bool wait_impl (unique_lock<windows7::mutex> & lock, DWORD time)330{331windows7::mutex * pmutex = lock.release();332bool success = wait_unique(pmutex, time);333lock = unique_lock<windows7::mutex>(*pmutex, adopt_lock);334return success;335}336public:337using native_handle_type = PCONDITION_VARIABLE;338native_handle_type native_handle (void)339{340return &cvariable_;341}342343condition_variable (void) = default;344~condition_variable (void) = default;345346condition_variable (const condition_variable &) = delete;347condition_variable & operator= (const condition_variable &) = delete;348349void notify_one (void) noexcept350{351WakeConditionVariable(&cvariable_);352}353354void notify_all (void) noexcept355{356WakeAllConditionVariable(&cvariable_);357}358359void wait (unique_lock<mutex> & lock)360{361wait_impl(lock, kInfinite);362}363364template<class Predicate>365void wait (unique_lock<mutex> & lock, Predicate pred)366{367while (!pred())368wait(lock);369}370371template <class Rep, class Period>372cv_status wait_for(unique_lock<mutex>& lock,373const std::chrono::duration<Rep, Period>& rel_time)374{375using namespace std::chrono;376auto timeout = duration_cast<milliseconds>(rel_time).count();377DWORD waittime = (timeout < kInfinite) ? ((timeout < 0) ? 0 : static_cast<DWORD>(timeout)) : (kInfinite - 1);378bool result = wait_impl(lock, waittime) || (timeout >= kInfinite);379return result ? cv_status::no_timeout : cv_status::timeout;380}381382template <class Rep, class Period, class Predicate>383bool wait_for(unique_lock<mutex>& lock,384const std::chrono::duration<Rep, Period>& rel_time,385Predicate pred)386{387return wait_until(lock,388std::chrono::steady_clock::now() + rel_time,389std::move(pred));390}391template <class Clock, class Duration>392cv_status wait_until (unique_lock<mutex>& lock,393const std::chrono::time_point<Clock,Duration>& abs_time)394{395return wait_for(lock, abs_time - Clock::now());396}397template <class Clock, class Duration, class Predicate>398bool wait_until (unique_lock<mutex>& lock,399const std::chrono::time_point<Clock, Duration>& abs_time,400Predicate pred)401{402while (!pred())403{404if (wait_until(lock, abs_time) == cv_status::timeout)405{406return pred();407}408}409return true;410}411};412413class condition_variable_any414{415static constexpr DWORD kInfinite = 0xffffffffl;416using native_shared_mutex = windows7::shared_mutex;417418condition_variable internal_cv_ {};419// When available, the SRW-based mutexes should be faster than the420// CriticalSection-based mutexes. Only try_lock will be unavailable in Vista,421// and try_lock is not used by condition_variable_any.422windows7::mutex internal_mutex_ {};423424template<class L>425bool wait_impl (L & lock, DWORD time)426{427unique_lock<decltype(internal_mutex_)> internal_lock(internal_mutex_);428lock.unlock();429bool success = internal_cv_.wait_impl(internal_lock, time);430lock.lock();431return success;432}433// If the lock happens to be called on a native Windows mutex, skip any extra434// contention.435inline bool wait_impl (unique_lock<mutex> & lock, DWORD time)436{437return internal_cv_.wait_impl(lock, time);438}439// Some shared_mutex functionality is available even in Vista, but it's not440// until Windows 7 that a full implementation is natively possible. The class441// itself is defined, with missing features, at the Vista feature level.442bool wait_impl (unique_lock<native_shared_mutex> & lock, DWORD time)443{444native_shared_mutex * pmutex = lock.release();445bool success = internal_cv_.wait_unique(pmutex, time);446lock = unique_lock<native_shared_mutex>(*pmutex, adopt_lock);447return success;448}449bool wait_impl (shared_lock<native_shared_mutex> & lock, DWORD time)450{451native_shared_mutex * pmutex = lock.release();452BOOL success = SleepConditionVariableSRW(native_handle(),453pmutex->native_handle(), time,454CONDITION_VARIABLE_LOCKMODE_SHARED);455lock = shared_lock<native_shared_mutex>(*pmutex, adopt_lock);456return success;457}458public:459using native_handle_type = typename condition_variable::native_handle_type;460461native_handle_type native_handle (void)462{463return internal_cv_.native_handle();464}465466void notify_one (void) noexcept467{468internal_cv_.notify_one();469}470471void notify_all (void) noexcept472{473internal_cv_.notify_all();474}475476condition_variable_any (void) = default;477~condition_variable_any (void) = default;478479template<class L>480void wait (L & lock)481{482wait_impl(lock, kInfinite);483}484485template<class L, class Predicate>486void wait (L & lock, Predicate pred)487{488while (!pred())489wait(lock);490}491492template <class L, class Rep, class Period>493cv_status wait_for(L& lock, const std::chrono::duration<Rep,Period>& period)494{495using namespace std::chrono;496auto timeout = duration_cast<milliseconds>(period).count();497DWORD waittime = (timeout < kInfinite) ? ((timeout < 0) ? 0 : static_cast<DWORD>(timeout)) : (kInfinite - 1);498bool result = wait_impl(lock, waittime) || (timeout >= kInfinite);499return result ? cv_status::no_timeout : cv_status::timeout;500}501502template <class L, class Rep, class Period, class Predicate>503bool wait_for(L& lock, const std::chrono::duration<Rep, Period>& period,504Predicate pred)505{506return wait_until(lock, std::chrono::steady_clock::now() + period,507std::move(pred));508}509template <class L, class Clock, class Duration>510cv_status wait_until (L& lock,511const std::chrono::time_point<Clock,Duration>& abs_time)512{513return wait_for(lock, abs_time - Clock::now());514}515template <class L, class Clock, class Duration, class Predicate>516bool wait_until (L& lock,517const std::chrono::time_point<Clock, Duration>& abs_time,518Predicate pred)519{520while (!pred())521{522if (wait_until(lock, abs_time) == cv_status::timeout)523{524return pred();525}526}527return true;528}529};530} // Namespace vista531#endif532#if WINVER < 0x0600533using xp::condition_variable;534using xp::condition_variable_any;535#else536using vista::condition_variable;537using vista::condition_variable_any;538#endif539} // Namespace mingw_stdthread540541// Push objects into std, but only if they are not already there.542namespace std543{544// Because of quirks of the compiler, the common "using namespace std;"545// directive would flatten the namespaces and introduce ambiguity where there546// was none. Direct specification (std::), however, would be unaffected.547// Take the safe option, and include only in the presence of MinGW's win32548// implementation.549#if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS) && !defined(__clang__)550using mingw_stdthread::cv_status;551using mingw_stdthread::condition_variable;552using mingw_stdthread::condition_variable_any;553#elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING) // Skip repetition554#define MINGW_STDTHREAD_REDUNDANCY_WARNING555#pragma message "This version of MinGW seems to include a win32 port of\556pthreads, and probably already has C++11 std threading classes implemented,\557based on pthreads. These classes, found in namespace std, are not overridden\558by the mingw-std-thread library. If you would still like to use this\559implementation (as it is more lightweight), use the classes provided in\560namespace mingw_stdthread."561#endif562}563#endif // MINGW_CONDITIONAL_VARIABLE_H564565566