diff options
Diffstat (limited to 'include')
-rw-r--r-- | include/common.hpp | 1 | ||||
-rw-r--r-- | include/mingw.condition_variable.h | 211 | ||||
-rw-r--r-- | include/mingw.mutex.h | 275 | ||||
-rw-r--r-- | include/mingw.thread.h | 167 |
4 files changed, 654 insertions, 0 deletions
diff --git a/include/common.hpp b/include/common.hpp index 71039c7..1178540 100644 --- a/include/common.hpp +++ b/include/common.hpp @@ -45,6 +45,7 @@ // windows stuff #ifdef __WIN32__ +using uint = unsigned int; #undef near #endif 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 diff --git a/include/mingw.mutex.h b/include/mingw.mutex.h new file mode 100644 index 0000000..d453202 --- /dev/null +++ b/include/mingw.mutex.h @@ -0,0 +1,275 @@ +/** +* @file mingw.mutex.h +* @brief std::mutex et al 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 WIN32STDMUTEX_H +#define WIN32STDMUTEX_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. \ + You are likely to have class redefinition errors below, and unfirtunately 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 +// Recursion checks on non-recursive locks have some performance penalty, so the user +// may want to disable the checks in release builds. In that case, make sure they +// are always enabled in debug builds. + +#if defined(STDMUTEX_NO_RECURSION_CHECKS) && !defined(NDEBUG) + #undef STDMUTEX_NO_RECURSION_CHECKS +#endif + +#include <windows.h> +#include <chrono> +#include <system_error> + +#ifndef EPROTO + #define EPROTO 134 +#endif +#ifndef EOWNERDEAD + #define EOWNERDEAD 133 +#endif + +namespace std +{ +class recursive_mutex +{ +protected: + CRITICAL_SECTION mHandle; +public: + typedef LPCRITICAL_SECTION native_handle_type; + native_handle_type native_handle() {return &mHandle;} + recursive_mutex() noexcept + { + InitializeCriticalSection(&mHandle); + } + recursive_mutex (const recursive_mutex&) = delete; + recursive_mutex& operator=(const recursive_mutex&) = delete; + ~recursive_mutex() noexcept + { + DeleteCriticalSection(&mHandle); + } + void lock() + { + EnterCriticalSection(&mHandle); + } + void unlock() + { + LeaveCriticalSection(&mHandle); + } + bool try_lock() + { + return (TryEnterCriticalSection(&mHandle)!=0); + } +}; +template <class B> +class _NonRecursive: protected B +{ +protected: + typedef B base; + DWORD mOwnerThread; +public: + using base::native_handle_type; + using base::native_handle; + _NonRecursive() noexcept :base(), mOwnerThread(0) {} + _NonRecursive (const _NonRecursive<B>&) = delete; + _NonRecursive& operator= (const _NonRecursive<B>&) = delete; + void lock() + { + base::lock(); + checkSetOwnerAfterLock(); + } +protected: + void checkSetOwnerAfterLock() + { + DWORD self = GetCurrentThreadId(); + if (mOwnerThread == self) + { + fprintf(stderr, "FATAL: Recursive locking or non-recursive mutex detected. Throwing sysetm exception\n"); + fflush(stderr); + throw system_error(EDEADLK, generic_category()); + } + mOwnerThread = self; + } + void checkSetOwnerBeforeUnlock() + { + DWORD self = GetCurrentThreadId(); + if (mOwnerThread != self) + { + fprintf(stderr, "FATAL: Recursive unlocking of non-recursive mutex detected. Throwing system exception\n"); + fflush(stderr); + throw system_error(EDEADLK, generic_category()); + } + mOwnerThread = 0; + } +public: + void unlock() + { + checkSetOwnerBeforeUnlock(); + base::unlock(); + } + bool try_lock() + { + bool ret = base::try_lock(); + if (ret) + checkSetOwnerAfterLock(); + return ret; + } +}; + +#ifndef STDMUTEX_NO_RECURSION_CHECKS + typedef _NonRecursive<recursive_mutex> mutex; +#else + typedef recursive_mutex mutex; +#endif + +class recursive_timed_mutex +{ +protected: + HANDLE mHandle; +public: + typedef HANDLE native_handle_type; + native_handle_type native_handle() const {return mHandle;} + recursive_timed_mutex(const recursive_timed_mutex&) = delete; + recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete; + recursive_timed_mutex(): mHandle(CreateMutex(NULL, FALSE, NULL)){} + ~recursive_timed_mutex() + { + CloseHandle(mHandle); + } + void lock() + { + DWORD ret = WaitForSingleObject(mHandle, INFINITE); + if (ret != WAIT_OBJECT_0) + { + if (ret == WAIT_ABANDONED) + throw system_error(EOWNERDEAD, generic_category()); + else + throw system_error(EPROTO, generic_category()); + } + } + void unlock() + { + if (!ReleaseMutex(mHandle)) + throw system_error(EDEADLK, generic_category()); + } + bool try_lock() + { + DWORD ret = WaitForSingleObject(mHandle, 0); + if (ret == WAIT_TIMEOUT) + return false; + else if (ret == WAIT_OBJECT_0) + return true; + else if (ret == WAIT_ABANDONED) + throw system_error(EOWNERDEAD, generic_category()); + else + throw system_error(EPROTO, generic_category()); + } + template <class Rep, class Period> + bool try_lock_for(const std::chrono::duration<Rep,Period>& dur) + { + DWORD timeout = (DWORD)chrono::duration_cast<chrono::milliseconds>(dur).count(); + + DWORD ret = WaitForSingleObject(mHandle, timeout); + if (ret == WAIT_TIMEOUT) + return false; + else if (ret == WAIT_OBJECT_0) + return true; + else if (ret == WAIT_ABANDONED) + throw system_error(EOWNERDEAD, generic_category()); + else + throw system_error(EPROTO, generic_category()); + } + template <class Clock, class Duration> + bool try_lock_until(const std::chrono::time_point<Clock,Duration>& timeout_time) + { + return try_lock_for(timeout_time - Clock::now()); + } +}; + +class timed_mutex: public _NonRecursive<recursive_timed_mutex> +{ +protected: + typedef _NonRecursive<recursive_timed_mutex> base; +public: + using base::base; + timed_mutex(const timed_mutex&) = delete; + timed_mutex& operator=(const timed_mutex&) = delete; + template <class Rep, class Period> + void try_lock_for(const std::chrono::duration<Rep,Period>& dur) + { + bool ret = base::try_lock_for(dur); +#ifndef STDMUTEX_NO_RECURSION_CHECKS + if (ret) + checkSetOwnerAfterLock(); +#endif + return ret; + } +public: + template <class Clock, class Duration> + bool try_lock_until(const std::chrono::time_point<Clock,Duration>& timeout_time) + { + bool ret = base::try_lock_until(timeout_time); +#ifndef STDMUTEX_NO_RECURSION_CHECKS + if (ret) + checkSetOwnerAfterLock(); +#endif + return ret; + } +}; +// You can use the scoped locks and other helpers that are still provided by <mutex> +// In that case, you must include <mutex> before including this file, so that this +// file will not try to redefine them +#ifndef _GLIBCXX_MUTEX + +/// Do not acquire ownership of the mutex. +struct defer_lock_t { }; + + /// Try to acquire ownership of the mutex without blocking. +struct try_to_lock_t { }; + + /// Assume the calling thread has already obtained mutex ownership + /// and manage it. +struct adopt_lock_t { }; + +constexpr defer_lock_t defer_lock { }; +constexpr try_to_lock_t try_to_lock { }; +constexpr adopt_lock_t adopt_lock { }; + +template <class M> +class lock_guard +{ +protected: + M& mMutex; +public: + typedef M mutex_type; + lock_guard(const lock_guard&) = delete; + lock_guard& operator=(const lock_guard&) = delete; + explicit lock_guard(mutex_type& m): mMutex(m) { mMutex.lock(); } + lock_guard(mutex_type& m, std::adopt_lock_t):mMutex(m){} + ~lock_guard() { mMutex.unlock(); } +}; + +#endif +} +#endif // WIN32STDMUTEX_H diff --git a/include/mingw.thread.h b/include/mingw.thread.h new file mode 100644 index 0000000..6c1a997 --- /dev/null +++ b/include/mingw.thread.h @@ -0,0 +1,167 @@ +/** +* @file mingw.thread.h +* @brief std::thread 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 WIN32STDTHREAD_H +#define WIN32STDTHREAD_H + +#include <windows.h> +#include <functional> +#include <memory> +#include <chrono> +#include <system_error> +#include <process.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 class redefinition errors below, 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 + +//instead of INVALID_HANDLE_VALUE _beginthreadex returns 0 +#define _STD_THREAD_INVALID_HANDLE 0 +namespace std +{ + +class thread +{ +public: + class id + { + DWORD mId; + void clear() {mId = 0;} + friend class thread; + public: + id(DWORD aId=0):mId(aId){} + bool operator==(const id& other) const {return mId == other.mId;} + }; +protected: + HANDLE mHandle; + id mThreadId; +public: + typedef HANDLE native_handle_type; + id get_id() const noexcept {return mThreadId;} + native_handle_type native_handle() const {return mHandle;} + thread(): mHandle(_STD_THREAD_INVALID_HANDLE){} + + thread(thread&& other) + :mHandle(other.mHandle), mThreadId(other.mThreadId) + { + other.mHandle = _STD_THREAD_INVALID_HANDLE; + other.mThreadId.clear(); + } + + thread(const thread &other)=delete; + + template<class Function, class... Args> + explicit thread(Function&& f, Args&&... args) + { + typedef decltype(std::bind(f, args...)) Call; + Call* call = new Call(std::bind(f, args...)); + mHandle = (HANDLE)_beginthreadex(NULL, 0, threadfunc<Call>, + (LPVOID)call, 0, (unsigned*)&(mThreadId.mId)); + } + template <class Call> + static unsigned int __stdcall threadfunc(void* arg) + { + std::unique_ptr<Call> upCall(static_cast<Call*>(arg)); + (*upCall)(); + return (unsigned long)0; + } + bool joinable() const {return mHandle != _STD_THREAD_INVALID_HANDLE;} + void join() + { + if (get_id() == GetCurrentThreadId()) + throw system_error(EDEADLK, generic_category()); + if (mHandle == _STD_THREAD_INVALID_HANDLE) + throw system_error(ESRCH, generic_category()); + if (!joinable()) + throw system_error(EINVAL, generic_category()); + WaitForSingleObject(mHandle, INFINITE); + CloseHandle(mHandle); + mHandle = _STD_THREAD_INVALID_HANDLE; + mThreadId.clear(); + } + + ~thread() + { + if (joinable()) + std::terminate(); + } + thread& operator=(const thread&) = delete; + thread& operator=(thread&& other) noexcept + { + if (joinable()) + std::terminate(); + swap(std::forward<thread>(other)); + return *this; + } + void swap(thread&& other) noexcept + { + std::swap(mHandle, other.mHandle); + std::swap(mThreadId.mId, other.mThreadId.mId); + } + static unsigned int hardware_concurrency() noexcept + { + static int ncpus = -1; + if (ncpus == -1) + { + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + ncpus = sysinfo.dwNumberOfProcessors; + } + return ncpus; + } + void detach() + { + if (!joinable()) + throw system_error(); + if (mHandle != _STD_THREAD_INVALID_HANDLE) + { + CloseHandle(mHandle); + mHandle = _STD_THREAD_INVALID_HANDLE; + } + mThreadId.clear(); + } +}; + +namespace this_thread +{ + inline thread::id get_id() {return thread::id(GetCurrentThreadId());} + inline void yield() {Sleep(0);} + template< class Rep, class Period > + void sleep_for( const std::chrono::duration<Rep,Period>& sleep_duration) + { + Sleep(chrono::duration_cast<chrono::milliseconds>(sleep_duration).count()); + } + template <class Clock, class Duration> + void sleep_until(const std::chrono::time_point<Clock,Duration>& sleep_time) + { + sleep_for(sleep_time-Clock::now()); + } +} + +} +#endif // WIN32STDTHREAD_H |