diff --git a/mingw.condition_variable.h b/mingw.condition_variable.h index 4067626..e8ccbd3 100644 --- a/mingw.condition_variable.h +++ b/mingw.condition_variable.h @@ -42,7 +42,6 @@ #include "mingw.mutex.h" #include "mingw.shared_mutex.h" -#include "mingw.throw.h" #if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0501) #error To use the MinGW-std-threads library, you will need to define the macro _WIN32_WINNT to be 0x0501 (Windows XP) or higher. @@ -79,12 +78,12 @@ class condition_variable_any : mSemaphore(CreateSemaphoreA(NULL, 0, 0xFFFF, NULL)) { if (mSemaphore == NULL) - throw_error(GetLastError(), std::generic_category()); + throw std::system_error(GetLastError(), std::generic_category()); mWakeEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (mWakeEvent == NULL) { CloseHandle(mSemaphore); - throw_error(GetLastError(), std::generic_category()); + throw std::system_error(GetLastError(), std::generic_category()); } } ~condition_variable_any() @@ -124,9 +123,8 @@ class condition_variable_any else { using namespace std; - throw_error(make_error_code(errc::protocol_error)); + throw system_error(make_error_code(errc::protocol_error)); } - return false; } public: template diff --git a/mingw.future.h b/mingw.future.h index e504bc2..04008a9 100644 --- a/mingw.future.h +++ b/mingw.future.h @@ -33,7 +33,6 @@ // Mutexes and condition variables are used explicitly. #include "mingw.mutex.h" #include "mingw.condition_variable.h" -#include "mingw.throw.h" #include #include @@ -178,7 +177,7 @@ struct FutureBase : public FutureStatic { #if !defined(NDEBUG) if (!valid()) - throw_error(future_errc::no_state); + throw future_error(future_errc::no_state); #endif // If there's already a value or exception, don't do any extraneous // synchronization. The `get()` method will do that for us. @@ -194,7 +193,7 @@ struct FutureBase : public FutureStatic { #if !defined(NDEBUG) if (!valid()) - throw_error(future_errc::no_state); + throw future_error(future_errc::no_state); #endif auto current_state = mState->mType.load(std::memory_order_relaxed); if (current_state & kReadyFlag) @@ -469,9 +468,9 @@ class promise : mingw_stdthread::detail::FutureBase void check_before_set (void) const { if (!valid()) - mingw_stdthread::throw_error(future_errc::no_state); + throw future_error(future_errc::no_state); if (mState->mType.load(std::memory_order_relaxed) & kSetFlag) - mingw_stdthread::throw_error(future_errc::promise_already_satisfied); + throw future_error(future_errc::promise_already_satisfied); } void check_abandon (void) @@ -496,13 +495,11 @@ class promise : mingw_stdthread::detail::FutureBase FALSE, // No need for this to be inherited. DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); if (!success) - mingw_stdthread::throw_error("MinGW STD Threads library failed to make a promise ready after thread exit."); + throw std::runtime_error("MinGW STD Threads library failed to make a promise ready after thread exit."); mState->increment_references(); bool handle_handled = false; -#if !MINGW_STDTHREAD_NO_EXCEPTIONS try { -#endif state_type * ptr = static_cast(mState); mingw_stdthread::thread watcher_thread ([ptr, thread_handle, &handle_handled](void) { @@ -531,27 +528,24 @@ class promise : mingw_stdthread::detail::FutureBase }); } watcher_thread.detach(); -#if !MINGW_STDTHREAD_NO_EXCEPTIONS } catch (...) { // Because the original promise is still alive, this can't be the decrement -// that destroys it. +// destroys it. mState->decrement_references(); if (!handle_handled) CloseHandle(thread_handle); - mingw_stdthread::throw_error("MinGW STD Threads library failed to make a promise ready after thread exit."); } -#endif } template future make_future (void) { if (!valid()) - mingw_stdthread::throw_error(future_errc::no_state); + throw future_error(future_errc::no_state); if (mRetrieved) - mingw_stdthread::throw_error(future_errc::future_already_retrieved); + throw future_error(future_errc::future_already_retrieved); mState->increment_references(); mRetrieved = true; return future(static_cast(mState)); @@ -935,15 +929,11 @@ struct StorageHelper template static void store_deferred (FutureState * state_ptr, Func && func, Args&&... args) { -#if MINGW_STDTHREAD_NO_EXCEPTIONS - state_ptr->set_value(invoke(std::forward(func), std::forward(args)...)); -#else try { state_ptr->set_value(invoke(std::forward(func), std::forward(args)...)); } catch (...) { state_ptr->set_exception(std::current_exception()); } -#endif } template static void store (FutureState * state_ptr, Func && func, Args&&... args) @@ -962,18 +952,13 @@ struct StorageHelper template static void store_deferred (FutureState * state_ptr, Func && func, Args&&... args) { - using Ref_non_cv = typename std::remove_cv::type; -#if MINGW_STDTHREAD_NO_EXCEPTIONS - Ref & rf = invoke(std::forward(func), std::forward(args)...); - state_ptr->set_value(const_cast(std::addressof(rf))); -#else try { + typedef typename std::remove_cv::type Ref_non_cv; Ref & rf = invoke(std::forward(func), std::forward(args)...); state_ptr->set_value(const_cast(std::addressof(rf))); } catch (...) { state_ptr->set_exception(std::current_exception()); } -#endif } template static void store (FutureState * state_ptr, Func && func, Args&&... args) @@ -992,17 +977,12 @@ struct StorageHelper template static void store_deferred (FutureState * state_ptr, Func && func, Args&&... args) { -#if MINGW_STDTHREAD_NO_EXCEPTIONS - invoke(std::forward(func), std::forward(args)...); - state_ptr->set_value(Empty{}); -#else try { invoke(std::forward(func), std::forward(args)...); state_ptr->set_value(Empty{}); } catch (...) { state_ptr->set_exception(std::current_exception()); } -#endif } template static void store (FutureState * state_ptr, Func && func, Args&&... args) diff --git a/mingw.mutex.h b/mingw.mutex.h index d15b91b..5548f16 100644 --- a/mingw.mutex.h +++ b/mingw.mutex.h @@ -1,487 +1,484 @@ -/** -* @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 - -#if !defined(__cplusplus) || (__cplusplus < 201103L) -#error A C++11 compiler is required! -#endif -// Recursion checks on non-recursive locks have some performance penalty, and -// the C++ standard does not mandate them. The user might want to explicitly -// enable or disable such checks. If the user has no preference, enable such -// checks in debug builds, but not in release builds. -#ifdef STDMUTEX_RECURSION_CHECKS -#elif defined(NDEBUG) -#define STDMUTEX_RECURSION_CHECKS 0 -#else -#define STDMUTEX_RECURSION_CHECKS 1 -#endif - - -#include -#include -#include -#include //need for call_once() - -#if STDMUTEX_RECURSION_CHECKS || !defined(NDEBUG) -#include -#endif - - -#include // Detect Windows version. -#if STDMUTEX_RECURSION_CHECKS -#include // For GetCurrentThreadId -#endif -#include // For InitializeCriticalSection, etc. -#include // For GetLastError -#include - -// Need for the implementation of invoke -#include "mingw.thread.h" -#include "mingw.throw.h" -#include "mingw.invoke.h" - -#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0501) -#error To use the MinGW-std-threads library, you will need to define the macro _WIN32_WINNT to be 0x0501 (Windows XP) or higher. -#endif - -namespace mingw_stdthread -{ -// The _NonRecursive class has mechanisms that do not play nice with direct -// manipulation of the native handle. This forward declaration is part of -// a friend class declaration. -#if STDMUTEX_RECURSION_CHECKS -namespace vista -{ -class condition_variable; -} -#endif -// To make this namespace equivalent to the thread-related subset of std, -// pull in the classes and class templates supplied by std but not by this -// implementation. -using std::lock_guard; -using std::unique_lock; -using std::adopt_lock_t; -using std::defer_lock_t; -using std::try_to_lock_t; -using std::adopt_lock; -using std::defer_lock; -using std::try_to_lock; - -class recursive_mutex -{ - CRITICAL_SECTION mHandle; -public: - typedef LPCRITICAL_SECTION native_handle_type; - native_handle_type native_handle() {return &mHandle;} - recursive_mutex() noexcept : mHandle() - { - 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); - } -}; - -#if STDMUTEX_RECURSION_CHECKS -struct _OwnerThread -{ -// If this is to be read before locking, then the owner-thread variable must -// be atomic to prevent a torn read from spuriously causing errors. - std::atomic mOwnerThread; - constexpr _OwnerThread () noexcept : mOwnerThread(0) {} - static void on_deadlock (void) - { - using namespace std; - fprintf(stderr, "FATAL: Recursive locking of non-recursive mutex\ - detected. Throwing system exception\n"); - fflush(stderr); - throw_error(make_error_code(errc::resource_deadlock_would_occur)); - } - DWORD checkOwnerBeforeLock() const - { - DWORD self = GetCurrentThreadId(); - if (mOwnerThread.load(std::memory_order_relaxed) == self) - on_deadlock(); - return self; - } - void setOwnerAfterLock(DWORD id) - { - mOwnerThread.store(id, std::memory_order_relaxed); - } - void checkSetOwnerBeforeUnlock() - { - DWORD self = GetCurrentThreadId(); - if (mOwnerThread.load(std::memory_order_relaxed) != self) - on_deadlock(); - mOwnerThread.store(0, std::memory_order_relaxed); - } -}; -#endif - -// Though the Slim Reader-Writer (SRW) locks used here are not complete until -// Windows 7, implementing partial functionality in Vista will simplify the -// interaction with condition variables. -#if defined(_WIN32) && (WINVER >= _WIN32_WINNT_VISTA) -namespace windows7 -{ -class mutex -{ - SRWLOCK mHandle; -// Track locking thread for error checking. -#if STDMUTEX_RECURSION_CHECKS - friend class vista::condition_variable; - _OwnerThread mOwnerThread {}; -#endif -public: - typedef PSRWLOCK native_handle_type; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" - constexpr mutex () noexcept : mHandle(SRWLOCK_INIT) { } -#pragma GCC diagnostic pop - mutex (const mutex&) = delete; - mutex & operator= (const mutex&) = delete; - void lock (void) - { -// Note: Undefined behavior if called recursively. -#if STDMUTEX_RECURSION_CHECKS - DWORD self = mOwnerThread.checkOwnerBeforeLock(); -#endif - AcquireSRWLockExclusive(&mHandle); -#if STDMUTEX_RECURSION_CHECKS - mOwnerThread.setOwnerAfterLock(self); -#endif - } - void unlock (void) - { -#if STDMUTEX_RECURSION_CHECKS - mOwnerThread.checkSetOwnerBeforeUnlock(); -#endif - ReleaseSRWLockExclusive(&mHandle); - } -// TryAcquireSRW functions are a Windows 7 feature. -#if (WINVER >= _WIN32_WINNT_WIN7) - bool try_lock (void) - { -#if STDMUTEX_RECURSION_CHECKS - DWORD self = mOwnerThread.checkOwnerBeforeLock(); -#endif - BOOL ret = TryAcquireSRWLockExclusive(&mHandle); -#if STDMUTEX_RECURSION_CHECKS - if (ret) - mOwnerThread.setOwnerAfterLock(self); -#endif - return ret; - } -#endif - native_handle_type native_handle (void) - { - return &mHandle; - } -}; -} // Namespace windows7 -#endif // Compiling for Vista -namespace xp -{ -class mutex -{ - CRITICAL_SECTION mHandle; - std::atomic_uchar mState; -// Track locking thread for error checking. -#if STDMUTEX_RECURSION_CHECKS - friend class vista::condition_variable; - _OwnerThread mOwnerThread {}; -#endif -public: - typedef PCRITICAL_SECTION native_handle_type; - constexpr mutex () noexcept : mHandle(), mState(2) { } - mutex (const mutex&) = delete; - mutex & operator= (const mutex&) = delete; - ~mutex() noexcept - { -// Undefined behavior if the mutex is held (locked) by any thread. -// Undefined behavior if a thread terminates while holding ownership of the -// mutex. - DeleteCriticalSection(&mHandle); - } - void lock (void) - { - unsigned char state = mState.load(std::memory_order_acquire); - while (state) { - if ((state == 2) && mState.compare_exchange_weak(state, 1, std::memory_order_acquire)) - { - InitializeCriticalSection(&mHandle); - mState.store(0, std::memory_order_release); - break; - } - if (state == 1) - { - Sleep(0); - state = mState.load(std::memory_order_acquire); - } - } -#if STDMUTEX_RECURSION_CHECKS - DWORD self = mOwnerThread.checkOwnerBeforeLock(); -#endif - EnterCriticalSection(&mHandle); -#if STDMUTEX_RECURSION_CHECKS - mOwnerThread.setOwnerAfterLock(self); -#endif - } - void unlock (void) - { -#if STDMUTEX_RECURSION_CHECKS - mOwnerThread.checkSetOwnerBeforeUnlock(); -#endif - LeaveCriticalSection(&mHandle); - } - bool try_lock (void) - { - unsigned char state = mState.load(std::memory_order_acquire); - if ((state == 2) && mState.compare_exchange_strong(state, 1, std::memory_order_acquire)) - { - InitializeCriticalSection(&mHandle); - mState.store(0, std::memory_order_release); - } - if (state == 1) - return false; -#if STDMUTEX_RECURSION_CHECKS - DWORD self = mOwnerThread.checkOwnerBeforeLock(); -#endif - BOOL ret = TryEnterCriticalSection(&mHandle); -#if STDMUTEX_RECURSION_CHECKS - if (ret) - mOwnerThread.setOwnerAfterLock(self); -#endif - return ret; - } - native_handle_type native_handle (void) - { - return &mHandle; - } -}; -} // Namespace "xp" -#if (WINVER >= _WIN32_WINNT_WIN7) -using windows7::mutex; -#else -using xp::mutex; -#endif - -class recursive_timed_mutex -{ - static constexpr DWORD kWaitAbandoned = 0x00000080l; - static constexpr DWORD kWaitObject0 = 0x00000000l; - static constexpr DWORD kInfinite = 0xffffffffl; - inline bool try_lock_internal (DWORD ms) noexcept - { - DWORD ret = WaitForSingleObject(mHandle, ms); -#ifndef NDEBUG - if (ret == kWaitAbandoned) - { - using namespace std; - fprintf(stderr, "FATAL: Thread terminated while holding a mutex."); - terminate(); - } -#endif - return (ret == kWaitObject0) || (ret == kWaitAbandoned); - } -protected: - HANDLE mHandle; -// Track locking thread for error checking of non-recursive timed_mutex. For -// standard compliance, this must be defined in same class and at the same -// access-control level as every other variable in the timed_mutex. -#if STDMUTEX_RECURSION_CHECKS - friend class vista::condition_variable; - _OwnerThread mOwnerThread {}; -#endif -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, kInfinite); -// If (ret == WAIT_ABANDONED), then the thread that held ownership was -// terminated. Behavior is undefined, but Windows will pass ownership to this -// thread. -#ifndef NDEBUG - if (ret == kWaitAbandoned) - { - using namespace std; - fprintf(stderr, "FATAL: Thread terminated while holding a mutex."); - terminate(); - } -#endif - if ((ret != kWaitObject0) && (ret != kWaitAbandoned)) - { - throw_error(GetLastError(), std::system_category()); - } - } - void unlock() - { - if (!ReleaseMutex(mHandle)) - throw_error(GetLastError(), std::system_category()); - } - bool try_lock() - { - return try_lock_internal(0); - } - template - bool try_lock_for(const std::chrono::duration& dur) - { - using namespace std::chrono; - auto timeout = duration_cast(dur).count(); - while (timeout > 0) - { - constexpr auto kMaxStep = static_cast(kInfinite-1); - auto step = (timeout < kMaxStep) ? timeout : kMaxStep; - if (try_lock_internal(static_cast(step))) - return true; - timeout -= step; - } - return false; - } - template - bool try_lock_until(const std::chrono::time_point& timeout_time) - { - return try_lock_for(timeout_time - Clock::now()); - } -}; - -// Override if, and only if, it is necessary for error-checking. -#if STDMUTEX_RECURSION_CHECKS -class timed_mutex: recursive_timed_mutex -{ -public: - timed_mutex(const timed_mutex&) = delete; - timed_mutex& operator=(const timed_mutex&) = delete; - void lock() - { - DWORD self = mOwnerThread.checkOwnerBeforeLock(); - recursive_timed_mutex::lock(); - mOwnerThread.setOwnerAfterLock(self); - } - void unlock() - { - mOwnerThread.checkSetOwnerBeforeUnlock(); - recursive_timed_mutex::unlock(); - } - template - bool try_lock_for(const std::chrono::duration& dur) - { - DWORD self = mOwnerThread.checkOwnerBeforeLock(); - bool ret = recursive_timed_mutex::try_lock_for(dur); - if (ret) - mOwnerThread.setOwnerAfterLock(self); - return ret; - } - template - bool try_lock_until(const std::chrono::time_point& timeout_time) - { - return try_lock_for(timeout_time - Clock::now()); - } - bool try_lock () - { - return try_lock_for(std::chrono::milliseconds(0)); - } -}; -#else -typedef recursive_timed_mutex timed_mutex; -#endif - -class once_flag -{ -// When available, the SRW-based mutexes should be faster than the -// CriticalSection-based mutexes. Only try_lock will be unavailable in Vista, -// and try_lock is not used by once_flag. -#if (_WIN32_WINNT == _WIN32_WINNT_VISTA) - windows7::mutex mMutex; -#else - mutex mMutex; -#endif - std::atomic_bool mHasRun; - once_flag(const once_flag&) = delete; - once_flag& operator=(const once_flag&) = delete; - template - friend void call_once(once_flag& once, Callable&& f, Args&&... args); -public: - constexpr once_flag() noexcept: mMutex(), mHasRun(false) {} -}; - -template -void call_once(once_flag& flag, Callable&& func, Args&&... args) -{ - if (flag.mHasRun.load(std::memory_order_acquire)) - return; - lock_guard lock(flag.mMutex); - if (flag.mHasRun.load(std::memory_order_acquire)) - return; - detail::invoke(std::forward(func),std::forward(args)...); - flag.mHasRun.store(true, std::memory_order_release); -} -} // Namespace mingw_stdthread - -// Push objects into std, but only if they are not already there. -namespace std -{ -// Because of quirks of the compiler, the common "using namespace std;" -// directive would flatten the namespaces and introduce ambiguity where there -// was none. Direct specification (std::), however, would be unaffected. -// Take the safe option, and include only in the presence of MinGW's win32 -// implementation. -#if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS) -using mingw_stdthread::recursive_mutex; -using mingw_stdthread::mutex; -using mingw_stdthread::recursive_timed_mutex; -using mingw_stdthread::timed_mutex; -using mingw_stdthread::once_flag; -using mingw_stdthread::call_once; -#elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING) // Skip repetition -#define MINGW_STDTHREAD_REDUNDANCY_WARNING -#pragma message "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. These classes, found in namespace std, are not overridden\ - by the mingw-std-thread library. If you would still like to use this\ - implementation (as it is more lightweight), use the classes provided in\ - namespace mingw_stdthread." -#endif -} -#endif // WIN32STDMUTEX_H +/** +* @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 + +#if !defined(__cplusplus) || (__cplusplus < 201103L) +#error A C++11 compiler is required! +#endif +// Recursion checks on non-recursive locks have some performance penalty, and +// the C++ standard does not mandate them. The user might want to explicitly +// enable or disable such checks. If the user has no preference, enable such +// checks in debug builds, but not in release builds. +#ifdef STDMUTEX_RECURSION_CHECKS +#elif defined(NDEBUG) +#define STDMUTEX_RECURSION_CHECKS 0 +#else +#define STDMUTEX_RECURSION_CHECKS 1 +#endif + +#include +#include +#include +#include //need for call_once() + +#if STDMUTEX_RECURSION_CHECKS || !defined(NDEBUG) +#include +#endif + + +#include // Detect Windows version. +#if STDMUTEX_RECURSION_CHECKS +#include // For GetCurrentThreadId +#endif +#include // For InitializeCriticalSection, etc. +#include // For GetLastError +#include + +// Need for the implementation of invoke +#include "mingw.invoke.h" + +#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0501) +#error To use the MinGW-std-threads library, you will need to define the macro _WIN32_WINNT to be 0x0501 (Windows XP) or higher. +#endif + +namespace mingw_stdthread +{ +// The _NonRecursive class has mechanisms that do not play nice with direct +// manipulation of the native handle. This forward declaration is part of +// a friend class declaration. +#if STDMUTEX_RECURSION_CHECKS +namespace vista +{ +class condition_variable; +} +#endif +// To make this namespace equivalent to the thread-related subset of std, +// pull in the classes and class templates supplied by std but not by this +// implementation. +using std::lock_guard; +using std::unique_lock; +using std::adopt_lock_t; +using std::defer_lock_t; +using std::try_to_lock_t; +using std::adopt_lock; +using std::defer_lock; +using std::try_to_lock; + +class recursive_mutex +{ + CRITICAL_SECTION mHandle; +public: + typedef LPCRITICAL_SECTION native_handle_type; + native_handle_type native_handle() {return &mHandle;} + recursive_mutex() noexcept : mHandle() + { + 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); + } +}; + +#if STDMUTEX_RECURSION_CHECKS +struct _OwnerThread +{ +// If this is to be read before locking, then the owner-thread variable must +// be atomic to prevent a torn read from spuriously causing errors. + std::atomic mOwnerThread; + constexpr _OwnerThread () noexcept : mOwnerThread(0) {} + static void on_deadlock (void) + { + using namespace std; + fprintf(stderr, "FATAL: Recursive locking of non-recursive mutex\ + detected. Throwing system exception\n"); + fflush(stderr); + throw system_error(make_error_code(errc::resource_deadlock_would_occur)); + } + DWORD checkOwnerBeforeLock() const + { + DWORD self = GetCurrentThreadId(); + if (mOwnerThread.load(std::memory_order_relaxed) == self) + on_deadlock(); + return self; + } + void setOwnerAfterLock(DWORD id) + { + mOwnerThread.store(id, std::memory_order_relaxed); + } + void checkSetOwnerBeforeUnlock() + { + DWORD self = GetCurrentThreadId(); + if (mOwnerThread.load(std::memory_order_relaxed) != self) + on_deadlock(); + mOwnerThread.store(0, std::memory_order_relaxed); + } +}; +#endif + +// Though the Slim Reader-Writer (SRW) locks used here are not complete until +// Windows 7, implementing partial functionality in Vista will simplify the +// interaction with condition variables. +#if defined(_WIN32) && (WINVER >= _WIN32_WINNT_VISTA) +namespace windows7 +{ +class mutex +{ + SRWLOCK mHandle; +// Track locking thread for error checking. +#if STDMUTEX_RECURSION_CHECKS + friend class vista::condition_variable; + _OwnerThread mOwnerThread {}; +#endif +public: + typedef PSRWLOCK native_handle_type; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" + constexpr mutex () noexcept : mHandle(SRWLOCK_INIT) { } +#pragma GCC diagnostic pop + mutex (const mutex&) = delete; + mutex & operator= (const mutex&) = delete; + void lock (void) + { +// Note: Undefined behavior if called recursively. +#if STDMUTEX_RECURSION_CHECKS + DWORD self = mOwnerThread.checkOwnerBeforeLock(); +#endif + AcquireSRWLockExclusive(&mHandle); +#if STDMUTEX_RECURSION_CHECKS + mOwnerThread.setOwnerAfterLock(self); +#endif + } + void unlock (void) + { +#if STDMUTEX_RECURSION_CHECKS + mOwnerThread.checkSetOwnerBeforeUnlock(); +#endif + ReleaseSRWLockExclusive(&mHandle); + } +// TryAcquireSRW functions are a Windows 7 feature. +#if (WINVER >= _WIN32_WINNT_WIN7) + bool try_lock (void) + { +#if STDMUTEX_RECURSION_CHECKS + DWORD self = mOwnerThread.checkOwnerBeforeLock(); +#endif + BOOL ret = TryAcquireSRWLockExclusive(&mHandle); +#if STDMUTEX_RECURSION_CHECKS + if (ret) + mOwnerThread.setOwnerAfterLock(self); +#endif + return ret; + } +#endif + native_handle_type native_handle (void) + { + return &mHandle; + } +}; +} // Namespace windows7 +#endif // Compiling for Vista +namespace xp +{ +class mutex +{ + CRITICAL_SECTION mHandle; + std::atomic_uchar mState; +// Track locking thread for error checking. +#if STDMUTEX_RECURSION_CHECKS + friend class vista::condition_variable; + _OwnerThread mOwnerThread {}; +#endif +public: + typedef PCRITICAL_SECTION native_handle_type; + constexpr mutex () noexcept : mHandle(), mState(2) { } + mutex (const mutex&) = delete; + mutex & operator= (const mutex&) = delete; + ~mutex() noexcept + { +// Undefined behavior if the mutex is held (locked) by any thread. +// Undefined behavior if a thread terminates while holding ownership of the +// mutex. + DeleteCriticalSection(&mHandle); + } + void lock (void) + { + unsigned char state = mState.load(std::memory_order_acquire); + while (state) { + if ((state == 2) && mState.compare_exchange_weak(state, 1, std::memory_order_acquire)) + { + InitializeCriticalSection(&mHandle); + mState.store(0, std::memory_order_release); + break; + } + if (state == 1) + { + Sleep(0); + state = mState.load(std::memory_order_acquire); + } + } +#if STDMUTEX_RECURSION_CHECKS + DWORD self = mOwnerThread.checkOwnerBeforeLock(); +#endif + EnterCriticalSection(&mHandle); +#if STDMUTEX_RECURSION_CHECKS + mOwnerThread.setOwnerAfterLock(self); +#endif + } + void unlock (void) + { +#if STDMUTEX_RECURSION_CHECKS + mOwnerThread.checkSetOwnerBeforeUnlock(); +#endif + LeaveCriticalSection(&mHandle); + } + bool try_lock (void) + { + unsigned char state = mState.load(std::memory_order_acquire); + if ((state == 2) && mState.compare_exchange_strong(state, 1, std::memory_order_acquire)) + { + InitializeCriticalSection(&mHandle); + mState.store(0, std::memory_order_release); + } + if (state == 1) + return false; +#if STDMUTEX_RECURSION_CHECKS + DWORD self = mOwnerThread.checkOwnerBeforeLock(); +#endif + BOOL ret = TryEnterCriticalSection(&mHandle); +#if STDMUTEX_RECURSION_CHECKS + if (ret) + mOwnerThread.setOwnerAfterLock(self); +#endif + return ret; + } + native_handle_type native_handle (void) + { + return &mHandle; + } +}; +} // Namespace "xp" +#if (WINVER >= _WIN32_WINNT_WIN7) +using windows7::mutex; +#else +using xp::mutex; +#endif + +class recursive_timed_mutex +{ + static constexpr DWORD kWaitAbandoned = 0x00000080l; + static constexpr DWORD kWaitObject0 = 0x00000000l; + static constexpr DWORD kInfinite = 0xffffffffl; + inline bool try_lock_internal (DWORD ms) noexcept + { + DWORD ret = WaitForSingleObject(mHandle, ms); +#ifndef NDEBUG + if (ret == kWaitAbandoned) + { + using namespace std; + fprintf(stderr, "FATAL: Thread terminated while holding a mutex."); + terminate(); + } +#endif + return (ret == kWaitObject0) || (ret == kWaitAbandoned); + } +protected: + HANDLE mHandle; +// Track locking thread for error checking of non-recursive timed_mutex. For +// standard compliance, this must be defined in same class and at the same +// access-control level as every other variable in the timed_mutex. +#if STDMUTEX_RECURSION_CHECKS + friend class vista::condition_variable; + _OwnerThread mOwnerThread {}; +#endif +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, kInfinite); +// If (ret == WAIT_ABANDONED), then the thread that held ownership was +// terminated. Behavior is undefined, but Windows will pass ownership to this +// thread. +#ifndef NDEBUG + if (ret == kWaitAbandoned) + { + using namespace std; + fprintf(stderr, "FATAL: Thread terminated while holding a mutex."); + terminate(); + } +#endif + if ((ret != kWaitObject0) && (ret != kWaitAbandoned)) + { + throw std::system_error(GetLastError(), std::system_category()); + } + } + void unlock() + { + if (!ReleaseMutex(mHandle)) + throw std::system_error(GetLastError(), std::system_category()); + } + bool try_lock() + { + return try_lock_internal(0); + } + template + bool try_lock_for(const std::chrono::duration& dur) + { + using namespace std::chrono; + auto timeout = duration_cast(dur).count(); + while (timeout > 0) + { + constexpr auto kMaxStep = static_cast(kInfinite-1); + auto step = (timeout < kMaxStep) ? timeout : kMaxStep; + if (try_lock_internal(static_cast(step))) + return true; + timeout -= step; + } + return false; + } + template + bool try_lock_until(const std::chrono::time_point& timeout_time) + { + return try_lock_for(timeout_time - Clock::now()); + } +}; + +// Override if, and only if, it is necessary for error-checking. +#if STDMUTEX_RECURSION_CHECKS +class timed_mutex: recursive_timed_mutex +{ +public: + timed_mutex(const timed_mutex&) = delete; + timed_mutex& operator=(const timed_mutex&) = delete; + void lock() + { + DWORD self = mOwnerThread.checkOwnerBeforeLock(); + recursive_timed_mutex::lock(); + mOwnerThread.setOwnerAfterLock(self); + } + void unlock() + { + mOwnerThread.checkSetOwnerBeforeUnlock(); + recursive_timed_mutex::unlock(); + } + template + bool try_lock_for(const std::chrono::duration& dur) + { + DWORD self = mOwnerThread.checkOwnerBeforeLock(); + bool ret = recursive_timed_mutex::try_lock_for(dur); + if (ret) + mOwnerThread.setOwnerAfterLock(self); + return ret; + } + template + bool try_lock_until(const std::chrono::time_point& timeout_time) + { + return try_lock_for(timeout_time - Clock::now()); + } + bool try_lock () + { + return try_lock_for(std::chrono::milliseconds(0)); + } +}; +#else +typedef recursive_timed_mutex timed_mutex; +#endif + +class once_flag +{ +// When available, the SRW-based mutexes should be faster than the +// CriticalSection-based mutexes. Only try_lock will be unavailable in Vista, +// and try_lock is not used by once_flag. +#if (_WIN32_WINNT == _WIN32_WINNT_VISTA) + windows7::mutex mMutex; +#else + mutex mMutex; +#endif + std::atomic_bool mHasRun; + once_flag(const once_flag&) = delete; + once_flag& operator=(const once_flag&) = delete; + template + friend void call_once(once_flag& once, Callable&& f, Args&&... args); +public: + constexpr once_flag() noexcept: mMutex(), mHasRun(false) {} +}; + +template +void call_once(once_flag& flag, Callable&& func, Args&&... args) +{ + if (flag.mHasRun.load(std::memory_order_acquire)) + return; + lock_guard lock(flag.mMutex); + if (flag.mHasRun.load(std::memory_order_acquire)) + return; + detail::invoke(std::forward(func),std::forward(args)...); + flag.mHasRun.store(true, std::memory_order_release); +} +} // Namespace mingw_stdthread + +// Push objects into std, but only if they are not already there. +namespace std +{ +// Because of quirks of the compiler, the common "using namespace std;" +// directive would flatten the namespaces and introduce ambiguity where there +// was none. Direct specification (std::), however, would be unaffected. +// Take the safe option, and include only in the presence of MinGW's win32 +// implementation. +#if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS) +using mingw_stdthread::recursive_mutex; +using mingw_stdthread::mutex; +using mingw_stdthread::recursive_timed_mutex; +using mingw_stdthread::timed_mutex; +using mingw_stdthread::once_flag; +using mingw_stdthread::call_once; +#elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING) // Skip repetition +#define MINGW_STDTHREAD_REDUNDANCY_WARNING +#pragma message "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. These classes, found in namespace std, are not overridden\ + by the mingw-std-thread library. If you would still like to use this\ + implementation (as it is more lightweight), use the classes provided in\ + namespace mingw_stdthread." +#endif +} +#endif // WIN32STDMUTEX_H diff --git a/mingw.shared_mutex.h b/mingw.shared_mutex.h index 01d3d3a..e498de9 100644 --- a/mingw.shared_mutex.h +++ b/mingw.shared_mutex.h @@ -31,7 +31,6 @@ #error A C++11 compiler is required! #endif - #include // For descriptive errors. #include @@ -52,8 +51,7 @@ // For defer_lock_t, adopt_lock_t, and try_to_lock_t #include "mingw.mutex.h" // For this_thread::yield. -#include "mingw.thread.h" -#include "mingw.throw.h" +//#include "mingw.thread.h" // Might be able to use native Slim Reader-Writer (SRW) locks. #ifdef _WIN32 @@ -129,7 +127,7 @@ class shared_mutex using namespace std; #ifndef NDEBUG if (!(mCounter.fetch_sub(1, memory_order_release) & static_cast(~kWriteBit))) - throw_error(make_error_code(errc::operation_not_permitted)); + throw system_error(make_error_code(errc::operation_not_permitted)); #else mCounter.fetch_sub(1, memory_order_release); #endif @@ -182,7 +180,7 @@ class shared_mutex using namespace std; #ifndef NDEBUG if (mCounter.load(memory_order_relaxed) != kWriteBit) - throw_error(make_error_code(errc::operation_not_permitted)); + throw system_error(make_error_code(errc::operation_not_permitted)); #endif mCounter.store(0, memory_order_release); } @@ -312,9 +310,9 @@ class shared_lock { using namespace std; if (mMutex == nullptr) - throw_error(make_error_code(errc::operation_not_permitted)); + throw system_error(make_error_code(errc::operation_not_permitted)); if (mOwns) - throw_error(make_error_code(errc::resource_deadlock_would_occur)); + throw system_error(make_error_code(errc::resource_deadlock_would_occur)); } public: typedef Mutex mutex_type; @@ -427,7 +425,7 @@ class shared_lock { using namespace std; if (!mOwns) - throw_error(make_error_code(errc::operation_not_permitted)); + throw system_error(make_error_code(errc::operation_not_permitted)); mMutex->unlock_shared(); mOwns = false; } diff --git a/mingw.thread.h b/mingw.thread.h index 7c6019b..caf333e 100644 --- a/mingw.thread.h +++ b/mingw.thread.h @@ -1,331 +1,329 @@ -/** -* @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 - -#if !defined(__cplusplus) || (__cplusplus < 201103L) -#error A C++11 compiler is required! -#endif - -// Use the standard classes for std::, if available. -#include - -#include // For std::size_t -#include // Detect error type. -#include // For std::terminate -#include // For std::system_error -#include // For std::hash -#include // For std::tuple -#include // For sleep timing. -#include // For std::unique_ptr -#include // Stream output for thread ids. -#include // For std::swap, std::forward - -#include "mingw.invoke.h" - -#include // For WaitForSingleObject -#include // For CloseHandle, etc. -#include // For GetNativeSystemInfo -#include // For GetCurrentThreadId -#include // For _beginthreadex - -#ifndef NDEBUG -#include -#endif - -#include "mingw.throw.h" - -#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0501) -#error To use the MinGW-std-threads library, you will need to define the macro _WIN32_WINNT to be 0x0501 (Windows XP) or higher. -#endif - -// Instead of INVALID_HANDLE_VALUE, _beginthreadex returns 0. -namespace mingw_stdthread -{ -namespace detail -{ - template - struct IntSeq {}; - - template - struct GenIntSeq : GenIntSeq { }; - - template - struct GenIntSeq<0, S...> { typedef IntSeq type; }; - - // We can't define the Call struct in the function - the standard forbids template methods in that case - template - class ThreadFuncCall - { - typedef std::tuple Tuple; - Func mFunc; - Tuple mArgs; - - template - void callFunc(detail::IntSeq) - { - detail::invoke(std::forward(mFunc), std::get(std::forward(mArgs)) ...); - } - public: - ThreadFuncCall(Func&& aFunc, Args&&... aArgs) - :mFunc(std::forward(aFunc)), mArgs(std::forward(aArgs)...){} - - void callFunc() - { - callFunc(typename detail::GenIntSeq::type()); - } - }; - -} // Namespace "detail" - -class thread -{ -public: - class id - { - DWORD mId; - void clear() {mId = 0;} - friend class thread; - friend class std::hash; - public: - explicit id(DWORD aId=0) noexcept : mId(aId){} - friend bool operator==(id x, id y) noexcept {return x.mId == y.mId; } - friend bool operator!=(id x, id y) noexcept {return x.mId != y.mId; } - friend bool operator< (id x, id y) noexcept {return x.mId < y.mId; } - friend bool operator<=(id x, id y) noexcept {return x.mId <= y.mId; } - friend bool operator> (id x, id y) noexcept {return x.mId > y.mId; } - friend bool operator>=(id x, id y) noexcept {return x.mId >= y.mId; } - - template - friend std::basic_ostream<_CharT, _Traits>& - operator<<(std::basic_ostream<_CharT, _Traits>& __out, id __id) - { - if (__id.mId == 0) - { - return __out << "(invalid std::thread::id)"; - } - else - { - return __out << __id.mId; - } - } - }; -private: - static constexpr HANDLE kInvalidHandle = nullptr; - static constexpr DWORD kInfinite = 0xffffffffl; - HANDLE mHandle; - id mThreadId; - - template - static unsigned __stdcall threadfunc(void* arg) - { - std::unique_ptr call(static_cast(arg)); - call->callFunc(); - return 0; - } - - static unsigned int _hardware_concurrency_helper() noexcept - { - SYSTEM_INFO sysinfo; -// This is one of the few functions used by the library which has a nearly- -// equivalent function defined in earlier versions of Windows. Include the -// workaround, just as a reminder that it does exist. -#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) - ::GetNativeSystemInfo(&sysinfo); -#else - ::GetSystemInfo(&sysinfo); -#endif - return sysinfo.dwNumberOfProcessors; - } -public: - typedef HANDLE native_handle_type; - id get_id() const noexcept {return mThreadId;} - native_handle_type native_handle() const {return mHandle;} - thread(): mHandle(kInvalidHandle), mThreadId(){} - - thread(thread&& other) - :mHandle(other.mHandle), mThreadId(other.mThreadId) - { - other.mHandle = kInvalidHandle; - other.mThreadId.clear(); - } - - thread(const thread &other)=delete; - - template - explicit thread(Func&& func, Args&&... args) : mHandle(), mThreadId() - { - typedef detail::ThreadFuncCall Call; - auto call = new Call( - std::forward(func), std::forward(args)...); - auto int_handle = _beginthreadex(NULL, 0, threadfunc, - static_cast(call), 0, - reinterpret_cast(&(mThreadId.mId))); - if (int_handle == 0) - { - mHandle = kInvalidHandle; - int errnum = errno; - delete call; -// Note: Should only throw EINVAL, EAGAIN, EACCES - throw_error(errnum, std::generic_category()); - } else - mHandle = reinterpret_cast(int_handle); - } - - bool joinable() const {return mHandle != kInvalidHandle;} - -// Note: Due to lack of synchronization, this function has a race condition -// if called concurrently, which leads to undefined behavior. The same applies -// to all other member functions of this class, but this one is mentioned -// explicitly. - void join() - { - using namespace std; - if (get_id() == id(GetCurrentThreadId())) - throw_error(make_error_code(errc::resource_deadlock_would_occur)); - if (mHandle == kInvalidHandle) - throw_error(make_error_code(errc::no_such_process)); - if (!joinable()) - throw_error(make_error_code(errc::invalid_argument)); - WaitForSingleObject(mHandle, INFINITE); - CloseHandle(mHandle); - mHandle = kInvalidHandle; - mThreadId.clear(); - } - - ~thread() - { - if (joinable()) - { -#ifndef NDEBUG - std::printf("Error: Must join() or detach() a thread before \ -destroying it.\n"); -#endif - std::terminate(); - } - } - thread& operator=(const thread&) = delete; - thread& operator=(thread&& other) noexcept - { - if (joinable()) - { -#ifndef NDEBUG - std::printf("Error: Must join() or detach() a thread before \ -moving another thread to it.\n"); -#endif - std::terminate(); - } - swap(std::forward(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 unsigned int cached = _hardware_concurrency_helper(); - return cached; - } - - void detach() - { - if (!joinable()) - { - using namespace std; - throw_error(make_error_code(errc::invalid_argument)); - } - if (mHandle != kInvalidHandle) - { - CloseHandle(mHandle); - mHandle = kInvalidHandle; - } - mThreadId.clear(); - } -}; - -namespace this_thread -{ - inline thread::id get_id() noexcept {return thread::id(GetCurrentThreadId());} - inline void yield() noexcept {Sleep(0);} - template< class Rep, class Period > - void sleep_for( const std::chrono::duration& sleep_duration) - { - static constexpr DWORD kInfinite = 0xffffffffl; - using namespace std::chrono; - using rep = milliseconds::rep; - rep ms = duration_cast(sleep_duration).count(); - while (ms > 0) - { - constexpr rep kMaxRep = static_cast(kInfinite - 1); - auto sleepTime = (ms < kMaxRep) ? ms : kMaxRep; - Sleep(static_cast(sleepTime)); - ms -= sleepTime; - } - } - template - void sleep_until(const std::chrono::time_point& sleep_time) - { - sleep_for(sleep_time-Clock::now()); - } -} -} // Namespace mingw_stdthread - -namespace std -{ -// Because of quirks of the compiler, the common "using namespace std;" -// directive would flatten the namespaces and introduce ambiguity where there -// was none. Direct specification (std::), however, would be unaffected. -// Take the safe option, and include only in the presence of MinGW's win32 -// implementation. -#if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS) -using mingw_stdthread::thread; -// Remove ambiguity immediately, to avoid problems arising from the above. -//using std::thread; -namespace this_thread -{ -using namespace mingw_stdthread::this_thread; -} -#elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING) // Skip repetition -#define MINGW_STDTHREAD_REDUNDANCY_WARNING -#pragma message "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. These classes, found in namespace std, are not overridden\ - by the mingw-std-thread library. If you would still like to use this\ - implementation (as it is more lightweight), use the classes provided in\ - namespace mingw_stdthread." -#endif - -// Specialize hash for this implementation's thread::id, even if the -// std::thread::id already has a hash. -template<> -struct hash -{ - typedef mingw_stdthread::thread::id argument_type; - typedef size_t result_type; - size_t operator() (const argument_type & i) const noexcept - { - return i.mId; - } -}; -} -#endif // WIN32STDTHREAD_H +/** +* @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 + +#if !defined(__cplusplus) || (__cplusplus < 201103L) +#error A C++11 compiler is required! +#endif + +// Use the standard classes for std::, if available. +#include + +#include // For std::size_t +#include // Detect error type. +#include // For std::terminate +#include // For std::system_error +#include // For std::hash +#include // For std::tuple +#include // For sleep timing. +#include // For std::unique_ptr +#include // Stream output for thread ids. +#include // For std::swap, std::forward + +#include "mingw.invoke.h" + +#include // For WaitForSingleObject +#include // For CloseHandle, etc. +#include // For GetNativeSystemInfo +#include // For GetCurrentThreadId +#include // For _beginthreadex + +#ifndef NDEBUG +#include +#endif + +#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0501) +#error To use the MinGW-std-threads library, you will need to define the macro _WIN32_WINNT to be 0x0501 (Windows XP) or higher. +#endif + +// Instead of INVALID_HANDLE_VALUE, _beginthreadex returns 0. +namespace mingw_stdthread +{ +namespace detail +{ + template + struct IntSeq {}; + + template + struct GenIntSeq : GenIntSeq { }; + + template + struct GenIntSeq<0, S...> { typedef IntSeq type; }; + + // We can't define the Call struct in the function - the standard forbids template methods in that case + template + class ThreadFuncCall + { + typedef std::tuple Tuple; + Func mFunc; + Tuple mArgs; + + template + void callFunc(detail::IntSeq) + { + detail::invoke(std::forward(mFunc), std::get(std::forward(mArgs)) ...); + } + public: + ThreadFuncCall(Func&& aFunc, Args&&... aArgs) + :mFunc(std::forward(aFunc)), mArgs(std::forward(aArgs)...){} + + void callFunc() + { + callFunc(typename detail::GenIntSeq::type()); + } + }; + +} // Namespace "detail" + +class thread +{ +public: + class id + { + DWORD mId; + void clear() {mId = 0;} + friend class thread; + friend class std::hash; + public: + explicit id(DWORD aId=0) noexcept : mId(aId){} + friend bool operator==(id x, id y) noexcept {return x.mId == y.mId; } + friend bool operator!=(id x, id y) noexcept {return x.mId != y.mId; } + friend bool operator< (id x, id y) noexcept {return x.mId < y.mId; } + friend bool operator<=(id x, id y) noexcept {return x.mId <= y.mId; } + friend bool operator> (id x, id y) noexcept {return x.mId > y.mId; } + friend bool operator>=(id x, id y) noexcept {return x.mId >= y.mId; } + + template + friend std::basic_ostream<_CharT, _Traits>& + operator<<(std::basic_ostream<_CharT, _Traits>& __out, id __id) + { + if (__id.mId == 0) + { + return __out << "(invalid std::thread::id)"; + } + else + { + return __out << __id.mId; + } + } + }; +private: + static constexpr HANDLE kInvalidHandle = nullptr; + static constexpr DWORD kInfinite = 0xffffffffl; + HANDLE mHandle; + id mThreadId; + + template + static unsigned __stdcall threadfunc(void* arg) + { + std::unique_ptr call(static_cast(arg)); + call->callFunc(); + return 0; + } + + static unsigned int _hardware_concurrency_helper() noexcept + { + SYSTEM_INFO sysinfo; +// This is one of the few functions used by the library which has a nearly- +// equivalent function defined in earlier versions of Windows. Include the +// workaround, just as a reminder that it does exist. +#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) + ::GetNativeSystemInfo(&sysinfo); +#else + ::GetSystemInfo(&sysinfo); +#endif + return sysinfo.dwNumberOfProcessors; + } +public: + typedef HANDLE native_handle_type; + id get_id() const noexcept {return mThreadId;} + native_handle_type native_handle() const {return mHandle;} + thread(): mHandle(kInvalidHandle), mThreadId(){} + + thread(thread&& other) + :mHandle(other.mHandle), mThreadId(other.mThreadId) + { + other.mHandle = kInvalidHandle; + other.mThreadId.clear(); + } + + thread(const thread &other)=delete; + + template + explicit thread(Func&& func, Args&&... args) : mHandle(), mThreadId() + { + typedef detail::ThreadFuncCall Call; + auto call = new Call( + std::forward(func), std::forward(args)...); + auto int_handle = _beginthreadex(NULL, 0, threadfunc, + static_cast(call), 0, + reinterpret_cast(&(mThreadId.mId))); + if (int_handle == 0) + { + mHandle = kInvalidHandle; + int errnum = errno; + delete call; +// Note: Should only throw EINVAL, EAGAIN, EACCES + throw std::system_error(errnum, std::generic_category()); + } else + mHandle = reinterpret_cast(int_handle); + } + + bool joinable() const {return mHandle != kInvalidHandle;} + +// Note: Due to lack of synchronization, this function has a race condition +// if called concurrently, which leads to undefined behavior. The same applies +// to all other member functions of this class, but this one is mentioned +// explicitly. + void join() + { + using namespace std; + if (get_id() == id(GetCurrentThreadId())) + throw system_error(make_error_code(errc::resource_deadlock_would_occur)); + if (mHandle == kInvalidHandle) + throw system_error(make_error_code(errc::no_such_process)); + if (!joinable()) + throw system_error(make_error_code(errc::invalid_argument)); + WaitForSingleObject(mHandle, kInfinite); + CloseHandle(mHandle); + mHandle = kInvalidHandle; + mThreadId.clear(); + } + + ~thread() + { + if (joinable()) + { +#ifndef NDEBUG + std::printf("Error: Must join() or detach() a thread before \ +destroying it.\n"); +#endif + std::terminate(); + } + } + thread& operator=(const thread&) = delete; + thread& operator=(thread&& other) noexcept + { + if (joinable()) + { +#ifndef NDEBUG + std::printf("Error: Must join() or detach() a thread before \ +moving another thread to it.\n"); +#endif + std::terminate(); + } + swap(std::forward(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 unsigned int cached = _hardware_concurrency_helper(); + return cached; + } + + void detach() + { + if (!joinable()) + { + using namespace std; + throw system_error(make_error_code(errc::invalid_argument)); + } + if (mHandle != kInvalidHandle) + { + CloseHandle(mHandle); + mHandle = kInvalidHandle; + } + mThreadId.clear(); + } +}; + +namespace this_thread +{ + inline thread::id get_id() noexcept {return thread::id(GetCurrentThreadId());} + inline void yield() noexcept {Sleep(0);} + template< class Rep, class Period > + void sleep_for( const std::chrono::duration& sleep_duration) + { + static constexpr DWORD kInfinite = 0xffffffffl; + using namespace std::chrono; + using rep = milliseconds::rep; + rep ms = duration_cast(sleep_duration).count(); + while (ms > 0) + { + constexpr rep kMaxRep = static_cast(kInfinite - 1); + auto sleepTime = (ms < kMaxRep) ? ms : kMaxRep; + Sleep(static_cast(sleepTime)); + ms -= sleepTime; + } + } + template + void sleep_until(const std::chrono::time_point& sleep_time) + { + sleep_for(sleep_time-Clock::now()); + } +} +} // Namespace mingw_stdthread + +namespace std +{ +// Because of quirks of the compiler, the common "using namespace std;" +// directive would flatten the namespaces and introduce ambiguity where there +// was none. Direct specification (std::), however, would be unaffected. +// Take the safe option, and include only in the presence of MinGW's win32 +// implementation. +#if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS) +using mingw_stdthread::thread; +// Remove ambiguity immediately, to avoid problems arising from the above. +//using std::thread; +namespace this_thread +{ +using namespace mingw_stdthread::this_thread; +} +#elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING) // Skip repetition +#define MINGW_STDTHREAD_REDUNDANCY_WARNING +#pragma message "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. These classes, found in namespace std, are not overridden\ + by the mingw-std-thread library. If you would still like to use this\ + implementation (as it is more lightweight), use the classes provided in\ + namespace mingw_stdthread." +#endif + +// Specialize hash for this implementation's thread::id, even if the +// std::thread::id already has a hash. +template<> +struct hash +{ + typedef mingw_stdthread::thread::id argument_type; + typedef size_t result_type; + size_t operator() (const argument_type & i) const noexcept + { + return i.mId; + } +}; +} +#endif // WIN32STDTHREAD_H diff --git a/mingw.throw.h b/mingw.throw.h deleted file mode 100644 index 23c33aa..0000000 --- a/mingw.throw.h +++ /dev/null @@ -1,55 +0,0 @@ -/** -* @file mingw.throw.h -* @brief throw helper to enable -fno-exceptions -* (c) 2013-2016 by Mega Limited, Auckland, New Zealand -* @author Maeiky -* -* @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_THROW_H_ -#define MINGW_THROW_H_ - -#if !defined(__cplusplus) || (__cplusplus < 201103L) -#error A C++11 compiler is required! -#endif - -#if (defined(__cpp_exceptions) && (__cpp_exceptions >= 199711L)) || \ - defined(__EXCEPTIONS) || (!defined(__clang__) && !defined(__GNUC__)) -#define MINGW_STDTHREAD_NO_EXCEPTIONS 0 -#else -#define MINGW_STDTHREAD_NO_EXCEPTIONS 1 -#endif - -// Disabling exceptions is very much non-standard behavior. Though C++20 may -// add a feature-test macro, earlier versions do not provide such a mechanism. -// Instead, appropriate compiler-specific macros must be checked. -namespace mingw_stdthread -{ -#if MINGW_STDTHREAD_NO_EXCEPTIONS -template -inline void throw_error (Args&&...) -{ - std::abort(); -} -#else -template -inline void throw_error (Args&&... args) -{ - throw T(std::forward(args)...); -} -#endif -} - - -#endif // MINGW_THROW_H_ diff --git a/tests/tests.cpp b/tests/tests.cpp index 39a0ef0..b4cb7ac 100644 --- a/tests/tests.cpp +++ b/tests/tests.cpp @@ -141,22 +141,28 @@ void test_future () future future_broken = promise_broken.get_future(); future future_late = promise_late.get_future(); -#if defined(__cpp_exceptions) && (__cpp_exceptions >= 199711L) try { future impossible_future = promise_value.get_future(); log("WARNING: Promise failed to detect that its future was already retrieved."); } catch(...) { log("\tPromise successfully prevented redundant future retrieval."); } -#endif log("\tPassing promises to a new thread..."); thread t ([](promise p_value, promise p_exception, promise, promise p_late) { this_thread::sleep_for(std::chrono::seconds(1)); - p_late.set_exception_at_thread_exit(std::make_exception_ptr(std::runtime_error("Thrown during the thread."))); + try { + throw std::runtime_error("Thrown during the thread."); + } catch (...) { + p_late.set_exception_at_thread_exit(std::current_exception()); + } test_future_set_value(p_value); - p_exception.set_exception(std::make_exception_ptr(std::runtime_error("Things happened as expected."))); + try { + throw std::runtime_error("Things happened as expected."); + } catch (...) { + p_exception.set_exception(std::current_exception()); + } this_thread::sleep_for(std::chrono::seconds(2)); }, std::move(promise_value), @@ -165,12 +171,9 @@ void test_future () std::move(promise_late)); t.detach(); -#if defined(__cpp_exceptions) && (__cpp_exceptions >= 199711L) try { -#endif bool was_expected = test_future_get_value(future_value); log("\tReceived %sexpected value.", (was_expected ? "" : "un")); -#if defined(__cpp_exceptions) && (__cpp_exceptions >= 199711L) } catch (...) { log("WARNING: Exception where there should be none!"); throw; @@ -181,10 +184,8 @@ void test_future () } catch (std::exception & e) { log("\tReceived an exception (\"%s\") as expected.", e.what()); } -#endif log("\tWaiting for the thread to exit..."); -#if defined(__cpp_exceptions) && (__cpp_exceptions >= 199711L) try { test_future_get_value(future_late); log("WARNING: Got a value where there should be an exception!"); @@ -198,7 +199,6 @@ void test_future () } catch (std::future_error & e) { log("\tReceived a future_error (\"%s\") as expected.", e.what()); } -#endif log("\tDeferring a function..."); auto async_deferred = async(launch::deferred, [] (void) -> T @@ -314,9 +314,7 @@ int main() } std::thread t([](TestMove&& a, const char* b, int c) mutable { -#if defined(__cpp_exceptions) && (__cpp_exceptions >= 199711L) try -#endif { log("Worker thread started, sleeping for a while..."); // Thread might move the string more than once. @@ -350,17 +348,13 @@ int main() log("Worker thread finishing"); } -#if defined(__cpp_exceptions) && (__cpp_exceptions >= 199711L) catch(std::exception& e) { printf("EXCEPTION in worker thread: %s\n", e.what()); } -#endif }, TestMove("move test"), "test message", -20); -#if defined(__cpp_exceptions) && (__cpp_exceptions >= 199711L) try -#endif { log("Main thread: Locking mutex, waiting on condvar..."); { @@ -389,12 +383,10 @@ int main() log("Main thread: Worker thread joined"); fflush(stdout); } -#if defined(__cpp_exceptions) && (__cpp_exceptions >= 199711L) catch(std::exception& e) { log("EXCEPTION in main thread: %s", e.what()); } -#endif once_flag of; call_once(of, test_call_once, 1, "test"); call_once(of, test_call_once, 1, "ERROR! Should not be called second time");