| @@ -1 +1 @@ | |||
| Subproject commit 3b826a31c4127df82e23cfd12c2dd14dc77a042d | |||
| Subproject commit 176f819c681405a12e7f6ecc1e2e1af472c3ed7b | |||
| @@ -18,6 +18,23 @@ This is a header-only library. To use, just include the corresponding `mingw.xxx | |||
| For example, `#include "mingw.thread.h"` replaces `#include <thread>`. | |||
| A `CMakeLists.txt` has also been provided. You can add it to your project by using `add_subdirectory()`, and then this library can be added as your targets' dependency by using `target_link_libraries(YOUR_TARGET PRIVATE mingw_stdthreads)`. By default it just adds a include path, allowing you to include headers using angle brackets (for example `#include <mingw.thread.h>`). But you can also provide options to let it generate "std-like" headers (see next paragraph). | |||
| Using "std-like" headers | |||
| ------------------------ | |||
| Probably you don't really want to replace all your includes from `#include <header>` to `#include "mingw.header.h"`. So if you are using GCC or clang, here are some ways to make you happy :) | |||
| With CMake, you just need to turn on the option `MINGW_STDTHREADS_GENERATE_STDHEADERS` before adding mingw-stdthreads, something like this: | |||
| ```CMake | |||
| option(MINGW_STDTHREADS_GENERATE_STDHEADERS "" ON) | |||
| add_subdirectory(mingw_stdthreads) | |||
| target_link_libraries(${TARGET} PRIVATE mingw_stdthreads) | |||
| ``` | |||
| When CMake generates project files, headers named in the "standard header" way will be generated and added to your include path. Then you can avoid stuffs like `mingw.thread.h`, and keep using `#include <thread>` like always. In addition, `MINGW_STDTHREADS_GENERATED_STDHEADERS` will be defined, you can use this macro to check if those generated headers are actually available. | |||
| If you aren't using CMake, you can use one of the three scripts inside [utility_scripts](utility_scripts) directory to manually generate those "std-like" headers. Note that this requires Microsoft Power Shell, so if you are cross-compiling, you would need to install Power Shell. | |||
| Compatibility | |||
| ------------- | |||
| @@ -25,8 +42,10 @@ This code has been tested to work with MinGW-w64 5.3.0, but should work with any | |||
| Switching from the win32-pthread based implementation | |||
| ----------------------------------------------------- | |||
| It seems that recent versions of MinGW-w64 include a Win32 port of pthreads, and have the `std::thread`, `std::mutex`, etc. classes implemented and working based on that compatibility | |||
| layer. | |||
| It seems that recent versions of MinGW-w64 include a Win32 port of pthreads, and have the `std::thread`, `std::mutex`, etc. classes implemented and working based on that compatibility layer. | |||
| You could use the built-in pthread implementation of Mingw by using the posix compiler, eg: `x86_64-w64-mingw32-g++-posix` (for Windows 64-bit). | |||
| That is a somewhat heavier implementation, as it relies on an abstraction layer, so you may still want to use this implementation for efficiency purposes. | |||
| Unfortunately you can't use this library standalone and independent of the system `<mutex>` headers, as it relies on those headers for `std::unique_lock` and other non-trivial utility classes. | |||
| In that case you will need to edit the `c++-config.h` file of your MinGW setup and comment out the definition of _GLIBCXX_HAS_GTHREADS. | |||
| @@ -3,4 +3,7 @@ | |||
| #pragma once | |||
| #include_next <condition_variable> | |||
| #if __GNUC__ < 12 | |||
| #include "mingw.condition_variable.h" | |||
| #endif | |||
| @@ -3,4 +3,7 @@ | |||
| #pragma once | |||
| #include_next <future> | |||
| #if __GNUC__ < 12 | |||
| #include "mingw.future.h" | |||
| #endif | |||
| @@ -3,4 +3,7 @@ | |||
| #pragma once | |||
| #include_next <invoke> | |||
| #if __GNUC__ < 12 | |||
| #include "mingw.invoke.h" | |||
| #endif | |||
| @@ -34,11 +34,20 @@ | |||
| #include <sdkddkver.h> // Detect Windows version. | |||
| #if (WINVER < _WIN32_WINNT_VISTA) | |||
| #include <atomic> | |||
| #endif | |||
| #if (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) | |||
| #pragma message "The Windows API that MinGW-w32 provides is not fully compatible\ | |||
| with Microsoft's API. We'll try to work around this, but we can make no\ | |||
| guarantees. This problem does not exist in MinGW-w64." | |||
| #include <windows.h> // No further granularity can be expected. | |||
| #else | |||
| #if (WINVER < _WIN32_WINNT_VISTA) | |||
| #include <windef.h> | |||
| #include <winbase.h> // For CreateSemaphore | |||
| #include <handleapi.h> | |||
| #endif | |||
| #include <synchapi.h> | |||
| #endif | |||
| #include "mingw.mutex.h" | |||
| #include "mingw.shared_mutex.h" | |||
| @@ -123,7 +132,7 @@ private: | |||
| else | |||
| { | |||
| using namespace std; | |||
| throw system_error(make_error_code((errc)EPROTO)); | |||
| throw system_error(make_error_code(errc::protocol_error)); | |||
| } | |||
| } | |||
| public: | |||
| @@ -92,7 +92,7 @@ namespace detail | |||
| inline static auto invoke (F&& f, Args&&... args) -> decltype(invoker::invoke(std::forward<F>(f), std::forward<Args>(args)...)) | |||
| { | |||
| return invoker::invoke(std::forward<F>(f), std::forward<Args>(args)...); | |||
| }; | |||
| } | |||
| }; | |||
| template<class F, class...Args> | |||
| @@ -43,14 +43,21 @@ | |||
| #include <cstdio> | |||
| #endif | |||
| #include <sdkddkver.h> // Detect Windows version. | |||
| #if (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) | |||
| #pragma message "The Windows API that MinGW-w32 provides is not fully compatible\ | |||
| with Microsoft's API. We'll try to work around this, but we can make no\ | |||
| guarantees. This problem does not exist in MinGW-w64." | |||
| #include <windows.h> // No further granularity can be expected. | |||
| #else | |||
| #if STDMUTEX_RECURSION_CHECKS | |||
| #include <processthreadsapi.h> // For GetCurrentThreadId | |||
| #endif | |||
| #include <synchapi.h> // For InitializeCriticalSection, etc. | |||
| #include <errhandlingapi.h> // For GetLastError | |||
| #include <handleapi.h> | |||
| #endif | |||
| // Need for the implementation of invoke | |||
| #include "mingw.invoke.h" | |||
| @@ -151,6 +158,14 @@ struct _OwnerThread | |||
| // 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. | |||
| //Define SRWLOCK_INIT. | |||
| #if !defined(SRWLOCK_INIT) | |||
| #pragma message "SRWLOCK_INIT macro is not defined. Defining automatically." | |||
| #define SRWLOCK_INIT {0} | |||
| #endif | |||
| #if defined(_WIN32) && (WINVER >= _WIN32_WINNT_VISTA) | |||
| namespace windows7 | |||
| { | |||
| @@ -388,6 +403,7 @@ public: | |||
| class timed_mutex: recursive_timed_mutex | |||
| { | |||
| public: | |||
| timed_mutex() = default; | |||
| timed_mutex(const timed_mutex&) = delete; | |||
| timed_mutex& operator=(const timed_mutex&) = delete; | |||
| void lock() | |||
| @@ -449,7 +465,7 @@ void call_once(once_flag& flag, Callable&& func, Args&&... args) | |||
| if (flag.mHasRun.load(std::memory_order_acquire)) | |||
| return; | |||
| lock_guard<decltype(flag.mMutex)> lock(flag.mMutex); | |||
| if (flag.mHasRun.load(std::memory_order_acquire)) | |||
| if (flag.mHasRun.load(std::memory_order_relaxed)) | |||
| return; | |||
| detail::invoke(std::forward<Callable>(func),std::forward<Args>(args)...); | |||
| flag.mHasRun.store(true, std::memory_order_release); | |||
| @@ -56,8 +56,15 @@ | |||
| // Might be able to use native Slim Reader-Writer (SRW) locks. | |||
| #ifdef _WIN32 | |||
| #include <sdkddkver.h> // Detect Windows version. | |||
| #if (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) | |||
| #pragma message "The Windows API that MinGW-w32 provides is not fully compatible\ | |||
| with Microsoft's API. We'll try to work around this, but we can make no\ | |||
| guarantees. This problem does not exist in MinGW-w64." | |||
| #include <windows.h> // No further granularity can be expected. | |||
| #else | |||
| #include <synchapi.h> | |||
| #endif | |||
| #endif | |||
| namespace mingw_stdthread | |||
| { | |||
| @@ -40,10 +40,17 @@ | |||
| #include "mingw.invoke.h" | |||
| #if (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) | |||
| #pragma message "The Windows API that MinGW-w32 provides is not fully compatible\ | |||
| with Microsoft's API. We'll try to work around this, but we can make no\ | |||
| guarantees. This problem does not exist in MinGW-w64." | |||
| #include <windows.h> // No further granularity can be expected. | |||
| #else | |||
| #include <synchapi.h> // For WaitForSingleObject | |||
| #include <handleapi.h> // For CloseHandle, etc. | |||
| #include <sysinfoapi.h> // For GetNativeSystemInfo | |||
| #include <processthreadsapi.h> // For GetCurrentThreadId | |||
| #endif | |||
| #include <process.h> // For _beginthreadex | |||
| #ifndef NDEBUG | |||
| @@ -68,20 +75,19 @@ namespace detail | |||
| template<std::size_t... S> | |||
| struct GenIntSeq<0, S...> { typedef IntSeq<S...> type; }; | |||
| // We can't define the Call struct in the function - the standard forbids template methods in that case | |||
| template<class Func, typename... Args> | |||
| class ThreadFuncCall | |||
| // Use a template specialization to avoid relying on compiler optimization | |||
| // when determining the parameter integer sequence. | |||
| template<class Func, class T, typename... Args> | |||
| class ThreadFuncCall; | |||
| // We can't define the Call struct in the function - the standard forbids template methods in that case | |||
| template<class Func, std::size_t... S, typename... Args> | |||
| class ThreadFuncCall<Func, detail::IntSeq<S...>, Args...> | |||
| { | |||
| static_assert(sizeof...(S) == sizeof...(Args), "Args must match."); | |||
| using Tuple = std::tuple<typename std::decay<Args>::type...>; | |||
| typename std::decay<Func>::type mFunc; | |||
| Tuple mArgs; | |||
| template <std::size_t... S> | |||
| void callFunc(detail::IntSeq<S...>) | |||
| { | |||
| // Note: Only called once (per thread) | |||
| detail::invoke(std::move(mFunc), std::move(std::get<S>(mArgs)) ...); | |||
| } | |||
| public: | |||
| ThreadFuncCall(Func&& aFunc, Args&&... aArgs) | |||
| : mFunc(std::forward<Func>(aFunc)), | |||
| @@ -91,10 +97,12 @@ namespace detail | |||
| void callFunc() | |||
| { | |||
| callFunc(typename detail::GenIntSeq<sizeof...(Args)>::type()); | |||
| detail::invoke(std::move(mFunc), std::move(std::get<S>(mArgs)) ...); | |||
| } | |||
| }; | |||
| // Allow construction of threads without exposing implementation. | |||
| class ThreadIdTool; | |||
| } // Namespace "detail" | |||
| class thread | |||
| @@ -102,12 +110,13 @@ class thread | |||
| public: | |||
| class id | |||
| { | |||
| DWORD mId; | |||
| void clear() {mId = 0;} | |||
| DWORD mId = 0; | |||
| friend class thread; | |||
| friend class std::hash<id>; | |||
| friend class detail::ThreadIdTool; | |||
| explicit id(DWORD aId) noexcept : mId(aId){} | |||
| public: | |||
| explicit id(DWORD aId=0) noexcept : mId(aId){} | |||
| id (void) noexcept = default; | |||
| 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; } | |||
| @@ -166,7 +175,7 @@ public: | |||
| :mHandle(other.mHandle), mThreadId(other.mThreadId) | |||
| { | |||
| other.mHandle = kInvalidHandle; | |||
| other.mThreadId.clear(); | |||
| other.mThreadId = id{}; | |||
| } | |||
| thread(const thread &other)=delete; | |||
| @@ -174,12 +183,13 @@ public: | |||
| template<class Func, typename... Args> | |||
| explicit thread(Func&& func, Args&&... args) : mHandle(), mThreadId() | |||
| { | |||
| typedef detail::ThreadFuncCall<Func, Args...> Call; | |||
| using ArgSequence = typename detail::GenIntSeq<sizeof...(Args)>::type; | |||
| using Call = detail::ThreadFuncCall<Func, ArgSequence, Args...>; | |||
| auto call = new Call( | |||
| std::forward<Func>(func), std::forward<Args>(args)...); | |||
| unsigned id_receiver; | |||
| auto int_handle = _beginthreadex(NULL, 0, threadfunc<Call>, | |||
| static_cast<LPVOID>(call), 0, | |||
| reinterpret_cast<unsigned*>(&(mThreadId.mId))); | |||
| static_cast<LPVOID>(call), 0, &id_receiver); | |||
| if (int_handle == 0) | |||
| { | |||
| mHandle = kInvalidHandle; | |||
| @@ -187,8 +197,10 @@ public: | |||
| delete call; | |||
| // Note: Should only throw EINVAL, EAGAIN, EACCES | |||
| throw std::system_error(errnum, std::generic_category()); | |||
| } else | |||
| } else { | |||
| mThreadId.mId = id_receiver; | |||
| mHandle = reinterpret_cast<HANDLE>(int_handle); | |||
| } | |||
| } | |||
| bool joinable() const {return mHandle != kInvalidHandle;} | |||
| @@ -209,7 +221,7 @@ public: | |||
| WaitForSingleObject(mHandle, kInfinite); | |||
| CloseHandle(mHandle); | |||
| mHandle = kInvalidHandle; | |||
| mThreadId.clear(); | |||
| mThreadId = id{}; | |||
| } | |||
| ~thread() | |||
| @@ -261,13 +273,28 @@ moving another thread to it.\n"); | |||
| CloseHandle(mHandle); | |||
| mHandle = kInvalidHandle; | |||
| } | |||
| mThreadId.clear(); | |||
| mThreadId = id{}; | |||
| } | |||
| }; | |||
| namespace detail | |||
| { | |||
| class ThreadIdTool | |||
| { | |||
| public: | |||
| static thread::id make_id (DWORD base_id) noexcept | |||
| { | |||
| return thread::id(base_id); | |||
| } | |||
| }; | |||
| } // Namespace "detail" | |||
| namespace this_thread | |||
| { | |||
| inline thread::id get_id() noexcept {return thread::id(GetCurrentThreadId());} | |||
| inline thread::id get_id() noexcept | |||
| { | |||
| return detail::ThreadIdTool::make_id(GetCurrentThreadId()); | |||
| } | |||
| inline void yield() noexcept {Sleep(0);} | |||
| template< class Rep, class Period > | |||
| void sleep_for( const std::chrono::duration<Rep,Period>& sleep_duration) | |||
| @@ -3,4 +3,7 @@ | |||
| #pragma once | |||
| #include_next <mutex> | |||
| #if __GNUC__ < 12 | |||
| #include "mingw.mutex.h" | |||
| #endif | |||
| @@ -3,4 +3,7 @@ | |||
| #pragma once | |||
| #include_next <shared_mutex> | |||
| #if __GNUC__ < 12 | |||
| #include "mingw.shared_mutex.h" | |||
| #endif | |||
| @@ -3,4 +3,7 @@ | |||
| #pragma once | |||
| #include_next <thread> | |||
| #if __GNUC__ < 12 | |||
| #include "mingw.thread.h" | |||
| #endif | |||