diff options
Diffstat (limited to 'include/mingw.condition_variable.h')
-rw-r--r-- | include/mingw.condition_variable.h | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/include/mingw.condition_variable.h b/include/mingw.condition_variable.h new file mode 100644 index 0000000..b664758 --- /dev/null +++ b/include/mingw.condition_variable.h @@ -0,0 +1,211 @@ +/** +* @file condition_variable.h +* @brief std::condition_variable implementation for MinGW +* +* (c) 2013-2016 by Mega Limited, Auckland, New Zealand +* @author Alexander Vassilev +* +* @copyright Simplified (2-clause) BSD License. +* You should have received a copy of the license along with this +* program. +* +* This code is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +* @note +* This file may become part of the mingw-w64 runtime package. If/when this happens, +* the appropriate license will be added, i.e. this code will become dual-licensed, +* and the current BSD 2-clause license will stay. +*/ + +#ifndef MINGW_CONDITIONAL_VARIABLE_H +#define MINGW_CONDITIONAL_VARIABLE_H +#include <atomic> +#include <assert.h> +#include "mingw.mutex.h" +#include <chrono> +#include <system_error> +#include <windows.h> +#ifdef _GLIBCXX_HAS_GTHREADS +#error This version of MinGW seems to include a win32 port of pthreads, and probably \ + already has C++11 std threading classes implemented, based on pthreads. \ + It is likely that you will get errors about redefined classes, and unfortunately \ + this implementation can not be used standalone and independent of the system <mutex>\ + header, since it relies on it for \ + std::unique_lock and other utility classes. If you would still like to use this \ + implementation (as it is more lightweight), you have to edit the \ + c++-config.h system header of your MinGW to not define _GLIBCXX_HAS_GTHREADS. \ + This will prevent system headers from defining actual threading classes while still \ + defining the necessary utility classes. +#endif + +namespace std +{ + +enum class cv_status { no_timeout, timeout }; +class condition_variable_any +{ +protected: + recursive_mutex mMutex; + atomic<int> mNumWaiters; + HANDLE mSemaphore; + HANDLE mWakeEvent; +public: + typedef HANDLE native_handle_type; + native_handle_type native_handle() {return mSemaphore;} + condition_variable_any(const condition_variable_any&) = delete; + condition_variable_any& operator=(const condition_variable_any&) = delete; + condition_variable_any() + :mNumWaiters(0), mSemaphore(CreateSemaphore(NULL, 0, 0xFFFF, NULL)), + mWakeEvent(CreateEvent(NULL, FALSE, FALSE, NULL)) + {} + ~condition_variable_any() { CloseHandle(mWakeEvent); CloseHandle(mSemaphore); } +protected: + template <class M> + bool wait_impl(M& lock, DWORD timeout) + { + { + lock_guard<recursive_mutex> guard(mMutex); + mNumWaiters++; + } + lock.unlock(); + DWORD ret = WaitForSingleObject(mSemaphore, timeout); + + mNumWaiters--; + SetEvent(mWakeEvent); + lock.lock(); + if (ret == WAIT_OBJECT_0) + return true; + else if (ret == WAIT_TIMEOUT) + return false; +//2 possible cases: +//1)The point in notify_all() where we determine the count to +//increment the semaphore with has not been reached yet: +//we just need to decrement mNumWaiters, but setting the event does not hurt +// +//2)Semaphore has just been released with mNumWaiters just before +//we decremented it. This means that the semaphore count +//after all waiters finish won't be 0 - because not all waiters +//woke up by acquiring the semaphore - we woke up by a timeout. +//The notify_all() must handle this grafecully +// + else + throw system_error(EPROTO, generic_category()); + } +public: + template <class M> + void wait(M& lock) + { + wait_impl(lock, INFINITE); + } + template <class M, class Predicate> + void wait(M& lock, Predicate pred) + { + while(!pred()) + { + wait(lock); + }; + } + + void notify_all() noexcept + { + lock_guard<recursive_mutex> lock(mMutex); //block any further wait requests until all current waiters are unblocked + if (mNumWaiters.load() <= 0) + return; + + ReleaseSemaphore(mSemaphore, mNumWaiters, NULL); + while(mNumWaiters > 0) + { + auto ret = WaitForSingleObject(mWakeEvent, 1000); + if ((ret == WAIT_FAILED) || (ret == WAIT_ABANDONED)) + throw system_error(EPROTO, generic_category()); + } + assert(mNumWaiters == 0); +//in case some of the waiters timed out just after we released the +//semaphore by mNumWaiters, it won't be zero now, because not all waiters +//woke up by acquiring the semaphore. So we must zero the semaphore before +//we accept waiters for the next event +//See _wait_impl for details + while(WaitForSingleObject(mSemaphore, 0) == WAIT_OBJECT_0); + } + void notify_one() noexcept + { + lock_guard<recursive_mutex> lock(mMutex); + if (!mNumWaiters) + return; + int targetWaiters = mNumWaiters.load() - 1; + ReleaseSemaphore(mSemaphore, 1, NULL); + while(mNumWaiters > targetWaiters) + { + auto ret = WaitForSingleObject(mWakeEvent, 1000); + if ((ret == WAIT_FAILED) || (ret == WAIT_ABANDONED)) + throw system_error(EPROTO, generic_category()); + } + assert(mNumWaiters == targetWaiters); + } + template <class M, class Rep, class Period> + std::cv_status wait_for(M& lock, + const std::chrono::duration<Rep, Period>& rel_time) + { + long long timeout = chrono::duration_cast<chrono::milliseconds>(rel_time).count(); + if (timeout < 0) + timeout = 0; + bool ret = wait_impl(lock, (DWORD)timeout); + return ret?cv_status::no_timeout:cv_status::timeout; + } + + template <class M, class Rep, class Period, class Predicate> + bool wait_for(M& lock, + const std::chrono::duration<Rep, Period>& rel_time, Predicate pred) + { + wait_for(lock, rel_time); + return pred(); + } + template <class M, class Clock, class Duration> + cv_status wait_until (M& lock, + const chrono::time_point<Clock,Duration>& abs_time) + { + return wait_for(lock, abs_time - Clock::now()); + } + template <class M, class Clock, class Duration, class Predicate> + bool wait_until (M& lock, + const std::chrono::time_point<Clock, Duration>& abs_time, + Predicate pred) + { + auto time = abs_time - Clock::now(); + if (time < 0) + return pred(); + else + return wait_for(lock, time, pred); + } +}; +class condition_variable: protected condition_variable_any +{ +protected: + typedef condition_variable_any base; +public: + using base::native_handle_type; + using base::native_handle; + using base::base; + using base::notify_all; + using base::notify_one; + void wait(unique_lock<mutex> &lock) + { base::wait(lock); } + template <class Predicate> + void wait(unique_lock<mutex>& lock, Predicate pred) + { base::wait(lock, pred); } + template <class Rep, class Period> + std::cv_status wait_for(unique_lock<mutex>& lock, const std::chrono::duration<Rep, Period>& rel_time) + { return base::wait_for(lock, rel_time); } + template <class Rep, class Period, class Predicate> + bool wait_for(unique_lock<mutex>& lock, const std::chrono::duration<Rep, Period>& rel_time, Predicate pred) + { return base::wait_for(lock, rel_time, pred); } + template <class Clock, class Duration> + cv_status wait_until (unique_lock<mutex>& lock, const chrono::time_point<Clock,Duration>& abs_time) + { return base::wait_for(lock, abs_time); } + template <class Clock, class Duration, class Predicate> + bool wait_until (unique_lock<mutex>& lock, const std::chrono::time_point<Clock, Duration>& abs_time, Predicate pred) + { return base::wait_until(lock, abs_time, pred); } +}; +} +#endif // MINGW_CONDITIONAL_VARIABLE_H |