aboutsummaryrefslogtreecommitdiffstats
path: root/include/mingw.condition_variable.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/mingw.condition_variable.h')
-rw-r--r--include/mingw.condition_variable.h211
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