Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/mingw-std-threads/mingw.thread.h
9905 views
1
/**
2
* @file mingw.thread.h
3
* @brief std::thread implementation for MinGW
4
* (c) 2013-2016 by Mega Limited, Auckland, New Zealand
5
* @author Alexander Vassilev
6
*
7
* @copyright Simplified (2-clause) BSD License.
8
* You should have received a copy of the license along with this
9
* program.
10
*
11
* This code is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
* @note
15
* This file may become part of the mingw-w64 runtime package. If/when this happens,
16
* the appropriate license will be added, i.e. this code will become dual-licensed,
17
* and the current BSD 2-clause license will stay.
18
*/
19
20
#ifndef WIN32STDTHREAD_H
21
#define WIN32STDTHREAD_H
22
23
#if !defined(__cplusplus) || (__cplusplus < 201103L)
24
#error A C++11 compiler is required!
25
#endif
26
27
// Use the standard classes for std::, if available.
28
#include <thread>
29
30
#include <cstddef> // For std::size_t
31
#include <cerrno> // Detect error type.
32
#include <exception> // For std::terminate
33
#include <system_error> // For std::system_error
34
#include <functional> // For std::hash
35
#include <tuple> // For std::tuple
36
#include <chrono> // For sleep timing.
37
#include <memory> // For std::unique_ptr
38
#include <iosfwd> // Stream output for thread ids.
39
#include <utility> // For std::swap, std::forward
40
41
#include "mingw.invoke.h"
42
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
49
#include <synchapi.h> // For WaitForSingleObject
50
#include <handleapi.h> // For CloseHandle, etc.
51
#include <sysinfoapi.h> // For GetNativeSystemInfo
52
#include <processthreadsapi.h> // For GetCurrentThreadId
53
#endif
54
#include <process.h> // For _beginthreadex
55
56
#ifndef NDEBUG
57
#include <cstdio>
58
#endif
59
60
#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0501)
61
#error To use the MinGW-std-threads library, you will need to define the macro _WIN32_WINNT to be 0x0501 (Windows XP) or higher.
62
#endif
63
64
// Instead of INVALID_HANDLE_VALUE, _beginthreadex returns 0.
65
namespace mingw_stdthread
66
{
67
namespace detail
68
{
69
template<std::size_t...>
70
struct IntSeq {};
71
72
template<std::size_t N, std::size_t... S>
73
struct GenIntSeq : GenIntSeq<N-1, N-1, S...> { };
74
75
template<std::size_t... S>
76
struct GenIntSeq<0, S...> { typedef IntSeq<S...> type; };
77
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...>
85
{
86
static_assert(sizeof...(S) == sizeof...(Args), "Args must match.");
87
using Tuple = std::tuple<typename std::decay<Args>::type...>;
88
typename std::decay<Func>::type mFunc;
89
Tuple mArgs;
90
91
public:
92
ThreadFuncCall(Func&& aFunc, Args&&... aArgs)
93
: mFunc(std::forward<Func>(aFunc)),
94
mArgs(std::forward<Args>(aArgs)...)
95
{
96
}
97
98
void callFunc()
99
{
100
detail::invoke(std::move(mFunc), std::move(std::get<S>(mArgs)) ...);
101
}
102
};
103
104
// Allow construction of threads without exposing implementation.
105
class ThreadIdTool;
106
} // Namespace "detail"
107
108
class thread
109
{
110
public:
111
class id
112
{
113
DWORD mId = 0;
114
friend class thread;
115
friend class std::hash<id>;
116
friend class detail::ThreadIdTool;
117
explicit id(DWORD aId) noexcept : mId(aId){}
118
public:
119
id (void) noexcept = default;
120
friend bool operator==(id x, id y) noexcept {return x.mId == y.mId; }
121
friend bool operator!=(id x, id y) noexcept {return x.mId != y.mId; }
122
friend bool operator< (id x, id y) noexcept {return x.mId < y.mId; }
123
friend bool operator<=(id x, id y) noexcept {return x.mId <= y.mId; }
124
friend bool operator> (id x, id y) noexcept {return x.mId > y.mId; }
125
friend bool operator>=(id x, id y) noexcept {return x.mId >= y.mId; }
126
127
template<class _CharT, class _Traits>
128
friend std::basic_ostream<_CharT, _Traits>&
129
operator<<(std::basic_ostream<_CharT, _Traits>& __out, id __id)
130
{
131
if (__id.mId == 0)
132
{
133
return __out << "(invalid std::thread::id)";
134
}
135
else
136
{
137
return __out << __id.mId;
138
}
139
}
140
};
141
private:
142
static constexpr HANDLE kInvalidHandle = nullptr;
143
static constexpr DWORD kInfinite = 0xffffffffl;
144
HANDLE mHandle;
145
id mThreadId;
146
147
template <class Call>
148
static unsigned __stdcall threadfunc(void* arg)
149
{
150
std::unique_ptr<Call> call(static_cast<Call*>(arg));
151
call->callFunc();
152
return 0;
153
}
154
155
static unsigned int _hardware_concurrency_helper() noexcept
156
{
157
SYSTEM_INFO sysinfo;
158
// This is one of the few functions used by the library which has a nearly-
159
// equivalent function defined in earlier versions of Windows. Include the
160
// workaround, just as a reminder that it does exist.
161
#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501)
162
::GetNativeSystemInfo(&sysinfo);
163
#else
164
::GetSystemInfo(&sysinfo);
165
#endif
166
return sysinfo.dwNumberOfProcessors;
167
}
168
public:
169
typedef HANDLE native_handle_type;
170
id get_id() const noexcept {return mThreadId;}
171
native_handle_type native_handle() const {return mHandle;}
172
thread(): mHandle(kInvalidHandle), mThreadId(){}
173
174
thread(thread&& other)
175
:mHandle(other.mHandle), mThreadId(other.mThreadId)
176
{
177
other.mHandle = kInvalidHandle;
178
other.mThreadId = id{};
179
}
180
181
thread(const thread &other)=delete;
182
183
template<class Func, typename... Args>
184
explicit thread(Func&& func, Args&&... args) : mHandle(), mThreadId()
185
{
186
using ArgSequence = typename detail::GenIntSeq<sizeof...(Args)>::type;
187
using Call = detail::ThreadFuncCall<Func, ArgSequence, Args...>;
188
auto call = new Call(
189
std::forward<Func>(func), std::forward<Args>(args)...);
190
unsigned id_receiver;
191
auto int_handle = _beginthreadex(NULL, 0, threadfunc<Call>,
192
static_cast<LPVOID>(call), 0, &id_receiver);
193
if (int_handle == 0)
194
{
195
mHandle = kInvalidHandle;
196
delete call;
197
// Note: Should only throw EINVAL, EAGAIN, EACCES
198
__builtin_trap();
199
} else {
200
mThreadId.mId = id_receiver;
201
mHandle = reinterpret_cast<HANDLE>(int_handle);
202
}
203
}
204
205
bool joinable() const {return mHandle != kInvalidHandle;}
206
207
// Note: Due to lack of synchronization, this function has a race condition
208
// if called concurrently, which leads to undefined behavior. The same applies
209
// to all other member functions of this class, but this one is mentioned
210
// explicitly.
211
void join()
212
{
213
using namespace std;
214
if (get_id() == id(GetCurrentThreadId()))
215
__builtin_trap();
216
if (mHandle == kInvalidHandle)
217
__builtin_trap();
218
if (!joinable())
219
__builtin_trap();
220
WaitForSingleObject(mHandle, kInfinite);
221
CloseHandle(mHandle);
222
mHandle = kInvalidHandle;
223
mThreadId = id{};
224
}
225
226
~thread()
227
{
228
if (joinable())
229
{
230
#ifndef NDEBUG
231
std::printf("Error: Must join() or detach() a thread before \
232
destroying it.\n");
233
#endif
234
std::terminate();
235
}
236
}
237
thread& operator=(const thread&) = delete;
238
thread& operator=(thread&& other) noexcept
239
{
240
if (joinable())
241
{
242
#ifndef NDEBUG
243
std::printf("Error: Must join() or detach() a thread before \
244
moving another thread to it.\n");
245
#endif
246
std::terminate();
247
}
248
swap(std::forward<thread>(other));
249
return *this;
250
}
251
void swap(thread&& other) noexcept
252
{
253
std::swap(mHandle, other.mHandle);
254
std::swap(mThreadId.mId, other.mThreadId.mId);
255
}
256
257
static unsigned int hardware_concurrency() noexcept
258
{
259
static unsigned int cached = _hardware_concurrency_helper();
260
return cached;
261
}
262
263
void detach()
264
{
265
if (!joinable())
266
{
267
using namespace std;
268
__builtin_trap();
269
}
270
if (mHandle != kInvalidHandle)
271
{
272
CloseHandle(mHandle);
273
mHandle = kInvalidHandle;
274
}
275
mThreadId = id{};
276
}
277
};
278
279
namespace detail
280
{
281
class ThreadIdTool
282
{
283
public:
284
static thread::id make_id (DWORD base_id) noexcept
285
{
286
return thread::id(base_id);
287
}
288
};
289
} // Namespace "detail"
290
291
namespace this_thread
292
{
293
inline thread::id get_id() noexcept
294
{
295
return detail::ThreadIdTool::make_id(GetCurrentThreadId());
296
}
297
inline void yield() noexcept {Sleep(0);}
298
template< class Rep, class Period >
299
void sleep_for( const std::chrono::duration<Rep,Period>& sleep_duration)
300
{
301
static constexpr DWORD kInfinite = 0xffffffffl;
302
using namespace std::chrono;
303
using rep = milliseconds::rep;
304
rep ms = duration_cast<milliseconds>(sleep_duration).count();
305
while (ms > 0)
306
{
307
constexpr rep kMaxRep = static_cast<rep>(kInfinite - 1);
308
auto sleepTime = (ms < kMaxRep) ? ms : kMaxRep;
309
Sleep(static_cast<DWORD>(sleepTime));
310
ms -= sleepTime;
311
}
312
}
313
template <class Clock, class Duration>
314
void sleep_until(const std::chrono::time_point<Clock,Duration>& sleep_time)
315
{
316
sleep_for(sleep_time-Clock::now());
317
}
318
}
319
} // Namespace mingw_stdthread
320
321
namespace std
322
{
323
// Because of quirks of the compiler, the common "using namespace std;"
324
// directive would flatten the namespaces and introduce ambiguity where there
325
// was none. Direct specification (std::), however, would be unaffected.
326
// Take the safe option, and include only in the presence of MinGW's win32
327
// implementation.
328
#if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS) && !defined(__clang__)
329
using mingw_stdthread::thread;
330
// Remove ambiguity immediately, to avoid problems arising from the above.
331
//using std::thread;
332
namespace this_thread
333
{
334
using namespace mingw_stdthread::this_thread;
335
}
336
#elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING) // Skip repetition
337
#define MINGW_STDTHREAD_REDUNDANCY_WARNING
338
#pragma message "This version of MinGW seems to include a win32 port of\
339
pthreads, and probably already has C++11 std threading classes implemented,\
340
based on pthreads. These classes, found in namespace std, are not overridden\
341
by the mingw-std-thread library. If you would still like to use this\
342
implementation (as it is more lightweight), use the classes provided in\
343
namespace mingw_stdthread."
344
#endif
345
346
// Specialize hash for this implementation's thread::id, even if the
347
// std::thread::id already has a hash.
348
template<>
349
struct hash<mingw_stdthread::thread::id>
350
{
351
typedef mingw_stdthread::thread::id argument_type;
352
typedef size_t result_type;
353
size_t operator() (const argument_type & i) const noexcept
354
{
355
return i.mId;
356
}
357
};
358
}
359
#endif // WIN32STDTHREAD_H
360
361