Skip to content

Commit f950802

Browse files
committed
Update mingw-std-threads
Signed-off-by: falkTX <falktx@falktx.com>
1 parent 462318c commit f950802

13 files changed

Lines changed: 124 additions & 28 deletions

File tree

‎libs/mingw-std-threads/README.md‎

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,34 @@ This is a header-only library. To use, just include the corresponding `mingw.xxx
1818

1919
For example, `#include "mingw.thread.h"` replaces `#include <thread>`.
2020

21+
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).
22+
23+
Using "std-like" headers
24+
------------------------
25+
26+
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 :)
27+
28+
With CMake, you just need to turn on the option `MINGW_STDTHREADS_GENERATE_STDHEADERS` before adding mingw-stdthreads, something like this:
29+
```CMake
30+
option(MINGW_STDTHREADS_GENERATE_STDHEADERS "" ON)
31+
add_subdirectory(mingw_stdthreads)
32+
target_link_libraries(${TARGET} PRIVATE mingw_stdthreads)
33+
```
34+
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.
35+
36+
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.
37+
2138
Compatibility
2239
-------------
2340

2441
This code has been tested to work with MinGW-w64 5.3.0, but should work with any other MinGW version that has the `std` threading classes missing, has C++11 support for lambda functions, variadic templates, and has working mutex helper classes in `<mutex>`.
2542

2643
Switching from the win32-pthread based implementation
2744
-----------------------------------------------------
28-
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
29-
layer.
45+
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.
46+
47+
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).
48+
3049
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.
3150
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.
3251
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.

‎libs/mingw-std-threads/condition_variable‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,7 @@
33

44
#pragma once
55
#include_next <condition_variable>
6+
7+
#if __GNUC__ < 12
68
#include "mingw.condition_variable.h"
9+
#endif

‎libs/mingw-std-threads/future‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,7 @@
33

44
#pragma once
55
#include_next <future>
6+
7+
#if __GNUC__ < 12
68
#include "mingw.future.h"
9+
#endif

‎libs/mingw-std-threads/invoke‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,7 @@
33

44
#pragma once
55
#include_next <invoke>
6+
7+
#if __GNUC__ < 12
68
#include "mingw.invoke.h"
9+
#endif

‎libs/mingw-std-threads/mingw.condition_variable.h‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,20 @@
3434
#include <sdkddkver.h> // Detect Windows version.
3535
#if (WINVER < _WIN32_WINNT_VISTA)
3636
#include <atomic>
37+
#endif
38+
#if (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))
39+
#pragma message "The Windows API that MinGW-w32 provides is not fully compatible\
40+
with Microsoft's API. We'll try to work around this, but we can make no\
41+
guarantees. This problem does not exist in MinGW-w64."
42+
#include <windows.h> // No further granularity can be expected.
43+
#else
44+
#if (WINVER < _WIN32_WINNT_VISTA)
3745
#include <windef.h>
3846
#include <winbase.h> // For CreateSemaphore
3947
#include <handleapi.h>
4048
#endif
4149
#include <synchapi.h>
50+
#endif
4251
4352
#include "mingw.mutex.h"
4453
#include "mingw.shared_mutex.h"
@@ -123,7 +132,7 @@ class condition_variable_any
123132
else
124133
{
125134
using namespace std;
126-
throw system_error(make_error_code((errc)EPROTO));
135+
throw system_error(make_error_code(errc::protocol_error));
127136
}
128137
}
129138
public:

‎libs/mingw-std-threads/mingw.invoke.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ namespace detail
9292
inline static auto invoke (F&& f, Args&&... args) -> decltype(invoker::invoke(std::forward<F>(f), std::forward<Args>(args)...))
9393
{
9494
return invoker::invoke(std::forward<F>(f), std::forward<Args>(args)...);
95-
};
95+
}
9696
};
9797

9898
template<class F, class...Args>

‎libs/mingw-std-threads/mingw.mutex.h‎

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,21 @@
4343
#include <cstdio>
4444
#endif
4545

46-
4746
#include <sdkddkver.h> // Detect Windows version.
47+
48+
#if (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))
49+
#pragma message "The Windows API that MinGW-w32 provides is not fully compatible\
50+
with Microsoft's API. We'll try to work around this, but we can make no\
51+
guarantees. This problem does not exist in MinGW-w64."
52+
#include <windows.h> // No further granularity can be expected.
53+
#else
4854
#if STDMUTEX_RECURSION_CHECKS
4955
#include <processthreadsapi.h> // For GetCurrentThreadId
5056
#endif
5157
#include <synchapi.h> // For InitializeCriticalSection, etc.
5258
#include <errhandlingapi.h> // For GetLastError
5359
#include <handleapi.h>
60+
#endif
5461
5562
// Need for the implementation of invoke
5663
#include "mingw.invoke.h"
@@ -151,6 +158,14 @@ struct _OwnerThread
151158
// Though the Slim Reader-Writer (SRW) locks used here are not complete until
152159
// Windows 7, implementing partial functionality in Vista will simplify the
153160
// interaction with condition variables.
161+
162+
//Define SRWLOCK_INIT.
163+
164+
#if !defined(SRWLOCK_INIT)
165+
#pragma message "SRWLOCK_INIT macro is not defined. Defining automatically."
166+
#define SRWLOCK_INIT {0}
167+
#endif
168+
154169
#if defined(_WIN32) && (WINVER >= _WIN32_WINNT_VISTA)
155170
namespace windows7
156171
{
@@ -388,6 +403,7 @@ class recursive_timed_mutex
388403
class timed_mutex: recursive_timed_mutex
389404
{
390405
public:
406+
timed_mutex() = default;
391407
timed_mutex(const timed_mutex&) = delete;
392408
timed_mutex& operator=(const timed_mutex&) = delete;
393409
void lock()
@@ -449,7 +465,7 @@ void call_once(once_flag& flag, Callable&& func, Args&&... args)
449465
if (flag.mHasRun.load(std::memory_order_acquire))
450466
return;
451467
lock_guard<decltype(flag.mMutex)> lock(flag.mMutex);
452-
if (flag.mHasRun.load(std::memory_order_acquire))
468+
if (flag.mHasRun.load(std::memory_order_relaxed))
453469
return;
454470
detail::invoke(std::forward<Callable>(func),std::forward<Args>(args)...);
455471
flag.mHasRun.store(true, std::memory_order_release);

‎libs/mingw-std-threads/mingw.shared_mutex.h‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,15 @@
5656
// Might be able to use native Slim Reader-Writer (SRW) locks.
5757
#ifdef _WIN32
5858
#include <sdkddkver.h> // Detect Windows version.
59+
#if (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))
60+
#pragma message "The Windows API that MinGW-w32 provides is not fully compatible\
61+
with Microsoft's API. We'll try to work around this, but we can make no\
62+
guarantees. This problem does not exist in MinGW-w64."
63+
#include <windows.h> // No further granularity can be expected.
64+
#else
5965
#include <synchapi.h>
6066
#endif
67+
#endif
6168
6269
namespace mingw_stdthread
6370
{

‎libs/mingw-std-threads/mingw.thread.h‎

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,17 @@
4040

4141
#include "mingw.invoke.h"
4242

43+
#if (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))
44+
#pragma message "The Windows API that MinGW-w32 provides is not fully compatible\
45+
with Microsoft's API. We'll try to work around this, but we can make no\
46+
guarantees. This problem does not exist in MinGW-w64."
47+
#include <windows.h> // No further granularity can be expected.
48+
#else
4349
#include <synchapi.h> // For WaitForSingleObject
4450
#include <handleapi.h> // For CloseHandle, etc.
4551
#include <sysinfoapi.h> // For GetNativeSystemInfo
4652
#include <processthreadsapi.h> // For GetCurrentThreadId
53+
#endif
4754
#include <process.h> // For _beginthreadex
4855
4956
#ifndef NDEBUG
@@ -68,20 +75,19 @@ namespace detail
6875
template<std::size_t... S>
6976
struct GenIntSeq<0, S...> { typedef IntSeq<S...> type; };
7077
71-
// We can't define the Call struct in the function - the standard forbids template methods in that case
72-
template<class Func, typename... Args>
73-
class ThreadFuncCall
78+
// Use a template specialization to avoid relying on compiler optimization
79+
// when determining the parameter integer sequence.
80+
template<class Func, class T, typename... Args>
81+
class ThreadFuncCall;
82+
// We can't define the Call struct in the function - the standard forbids template methods in that case
83+
template<class Func, std::size_t... S, typename... Args>
84+
class ThreadFuncCall<Func, detail::IntSeq<S...>, Args...>
7485
{
86+
static_assert(sizeof...(S) == sizeof...(Args), "Args must match.");
7587
using Tuple = std::tuple<typename std::decay<Args>::type...>;
7688
typename std::decay<Func>::type mFunc;
7789
Tuple mArgs;
7890
79-
template <std::size_t... S>
80-
void callFunc(detail::IntSeq<S...>)
81-
{
82-
// Note: Only called once (per thread)
83-
detail::invoke(std::move(mFunc), std::move(std::get<S>(mArgs)) ...);
84-
}
8591
public:
8692
ThreadFuncCall(Func&& aFunc, Args&&... aArgs)
8793
: mFunc(std::forward<Func>(aFunc)),
@@ -91,23 +97,26 @@ namespace detail
9197
9298
void callFunc()
9399
{
94-
callFunc(typename detail::GenIntSeq<sizeof...(Args)>::type());
100+
detail::invoke(std::move(mFunc), std::move(std::get<S>(mArgs)) ...);
95101
}
96102
};
97103
104+
// Allow construction of threads without exposing implementation.
105+
class ThreadIdTool;
98106
} // Namespace "detail"
99107
100108
class thread
101109
{
102110
public:
103111
class id
104112
{
105-
DWORD mId;
106-
void clear() {mId = 0;}
113+
DWORD mId = 0;
107114
friend class thread;
108115
friend class std::hash<id>;
116+
friend class detail::ThreadIdTool;
117+
explicit id(DWORD aId) noexcept : mId(aId){}
109118
public:
110-
explicit id(DWORD aId=0) noexcept : mId(aId){}
119+
id (void) noexcept = default;
111120
friend bool operator==(id x, id y) noexcept {return x.mId == y.mId; }
112121
friend bool operator!=(id x, id y) noexcept {return x.mId != y.mId; }
113122
friend bool operator< (id x, id y) noexcept {return x.mId < y.mId; }
@@ -166,29 +175,32 @@ class thread
166175
:mHandle(other.mHandle), mThreadId(other.mThreadId)
167176
{
168177
other.mHandle = kInvalidHandle;
169-
other.mThreadId.clear();
178+
other.mThreadId = id{};
170179
}
171180
172181
thread(const thread &other)=delete;
173182
174183
template<class Func, typename... Args>
175184
explicit thread(Func&& func, Args&&... args) : mHandle(), mThreadId()
176185
{
177-
typedef detail::ThreadFuncCall<Func, Args...> Call;
186+
using ArgSequence = typename detail::GenIntSeq<sizeof...(Args)>::type;
187+
using Call = detail::ThreadFuncCall<Func, ArgSequence, Args...>;
178188
auto call = new Call(
179189
std::forward<Func>(func), std::forward<Args>(args)...);
190+
unsigned id_receiver;
180191
auto int_handle = _beginthreadex(NULL, 0, threadfunc<Call>,
181-
static_cast<LPVOID>(call), 0,
182-
reinterpret_cast<unsigned*>(&(mThreadId.mId)));
192+
static_cast<LPVOID>(call), 0, &id_receiver);
183193
if (int_handle == 0)
184194
{
185195
mHandle = kInvalidHandle;
186196
int errnum = errno;
187197
delete call;
188198
// Note: Should only throw EINVAL, EAGAIN, EACCES
189199
throw std::system_error(errnum, std::generic_category());
190-
} else
200+
} else {
201+
mThreadId.mId = id_receiver;
191202
mHandle = reinterpret_cast<HANDLE>(int_handle);
203+
}
192204
}
193205
194206
bool joinable() const {return mHandle != kInvalidHandle;}
@@ -209,7 +221,7 @@ class thread
209221
WaitForSingleObject(mHandle, kInfinite);
210222
CloseHandle(mHandle);
211223
mHandle = kInvalidHandle;
212-
mThreadId.clear();
224+
mThreadId = id{};
213225
}
214226
215227
~thread()
@@ -261,13 +273,28 @@ moving another thread to it.\n");
261273
CloseHandle(mHandle);
262274
mHandle = kInvalidHandle;
263275
}
264-
mThreadId.clear();
276+
mThreadId = id{};
265277
}
266278
};
267279

280+
namespace detail
281+
{
282+
class ThreadIdTool
283+
{
284+
public:
285+
static thread::id make_id (DWORD base_id) noexcept
286+
{
287+
return thread::id(base_id);
288+
}
289+
};
290+
} // Namespace "detail"
291+
268292
namespace this_thread
269293
{
270-
inline thread::id get_id() noexcept {return thread::id(GetCurrentThreadId());}
294+
inline thread::id get_id() noexcept
295+
{
296+
return detail::ThreadIdTool::make_id(GetCurrentThreadId());
297+
}
271298
inline void yield() noexcept {Sleep(0);}
272299
template< class Rep, class Period >
273300
void sleep_for( const std::chrono::duration<Rep,Period>& sleep_duration)

0 commit comments

Comments
 (0)