-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
thread
386 lines (312 loc) · 11.3 KB
/
thread
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
// thread standard header
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#pragma once
#ifndef _THREAD_
#define _THREAD_
#include <yvals_core.h>
#if _STL_COMPILER_PREPROCESSOR
#include <chrono>
#include <memory>
#include <process.h>
#include <tuple>
#include <xthreads.h>
#if _HAS_CXX20
#include <compare>
#include <stop_token>
#endif // _HAS_CXX20
#ifdef _M_CEE_PURE
#error <thread> is not supported when compiling with /clr:pure.
#endif // _M_CEE_PURE
#pragma pack(push, _CRT_PACKING)
#pragma warning(push, _STL_WARNING_LEVEL)
#pragma warning(disable : _STL_DISABLED_WARNINGS)
_STL_DISABLE_CLANG_WARNINGS
#pragma push_macro("new")
#undef new
_STD_BEGIN
#if _HAS_CXX20
class jthread;
#endif // _HAS_CXX20
class thread { // class for observing and managing threads
public:
class id;
using native_handle_type = void*;
thread() noexcept : _Thr{} {}
private:
#if _HAS_CXX20
friend jthread;
#endif // _HAS_CXX20
template <class _Tuple, size_t... _Indices>
static unsigned int __stdcall _Invoke(void* _RawVals) noexcept /* terminates */ {
// adapt invoke of user's callable object to _beginthreadex's thread procedure
const unique_ptr<_Tuple> _FnVals(static_cast<_Tuple*>(_RawVals));
_Tuple& _Tup = *_FnVals;
_STD invoke(_STD move(_STD get<_Indices>(_Tup))...);
_Cnd_do_broadcast_at_thread_exit(); // TRANSITION, ABI
return 0;
}
template <class _Tuple, size_t... _Indices>
_NODISCARD static constexpr auto _Get_invoke(index_sequence<_Indices...>) noexcept {
return &_Invoke<_Tuple, _Indices...>;
}
template <class _Fn, class... _Args>
void _Start(_Fn&& _Fx, _Args&&... _Ax) {
using _Tuple = tuple<decay_t<_Fn>, decay_t<_Args>...>;
auto _Decay_copied = _STD make_unique<_Tuple>(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...);
constexpr auto _Invoker_proc = _Get_invoke<_Tuple>(make_index_sequence<1 + sizeof...(_Args)>{});
#pragma warning(push)
#pragma warning(disable : 5039) // pointer or reference to potentially throwing function passed to
// extern C function under -EHc. Undefined behavior may occur
// if this function throws an exception. (/Wall)
_Thr._Hnd =
reinterpret_cast<void*>(_CSTD _beginthreadex(nullptr, 0, _Invoker_proc, _Decay_copied.get(), 0, &_Thr._Id));
#pragma warning(pop)
if (_Thr._Hnd) { // ownership transferred to the thread
(void) _Decay_copied.release();
} else { // failed to start thread
_Thr._Id = 0;
_Throw_Cpp_error(_RESOURCE_UNAVAILABLE_TRY_AGAIN);
}
}
public:
template <class _Fn, class... _Args, enable_if_t<!is_same_v<_Remove_cvref_t<_Fn>, thread>, int> = 0>
_NODISCARD_CTOR explicit thread(_Fn&& _Fx, _Args&&... _Ax) {
_Start(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...);
}
~thread() noexcept {
if (joinable()) {
_STD terminate();
}
}
thread(thread&& _Other) noexcept : _Thr(_STD exchange(_Other._Thr, {})) {}
thread& operator=(thread&& _Other) noexcept {
if (joinable()) {
_STD terminate();
}
_Thr = _STD exchange(_Other._Thr, {});
return *this;
}
thread(const thread&) = delete;
thread& operator=(const thread&) = delete;
void swap(thread& _Other) noexcept {
_STD swap(_Thr, _Other._Thr);
}
_NODISCARD bool joinable() const noexcept {
return _Thr._Id != 0;
}
void join() {
if (!joinable()) {
_Throw_Cpp_error(_INVALID_ARGUMENT);
}
if (_Thr._Id == _Thrd_id()) {
_Throw_Cpp_error(_RESOURCE_DEADLOCK_WOULD_OCCUR);
}
if (_Thrd_join(_Thr, nullptr) != _Thrd_success) {
_Throw_Cpp_error(_NO_SUCH_PROCESS);
}
_Thr = {};
}
void detach() {
if (!joinable()) {
_Throw_Cpp_error(_INVALID_ARGUMENT);
}
_Check_C_return(_Thrd_detach(_Thr));
_Thr = {};
}
_NODISCARD id get_id() const noexcept;
_NODISCARD static unsigned int hardware_concurrency() noexcept {
return _Thrd_hardware_concurrency();
}
_NODISCARD native_handle_type native_handle() { // return Win32 HANDLE as void *
return _Thr._Hnd;
}
private:
_Thrd_t _Thr;
};
template <class _Rep, class _Period>
_NODISCARD auto _To_absolute_time(const chrono::duration<_Rep, _Period>& _Rel_time) noexcept {
constexpr auto _Zero = chrono::duration<_Rep, _Period>::zero();
const auto _Now = chrono::steady_clock::now();
decltype(_Now + _Rel_time) _Abs_time = _Now; // return common type
if (_Rel_time > _Zero) {
constexpr auto _Forever = (chrono::steady_clock::time_point::max)();
if (_Abs_time < _Forever - _Rel_time) {
_Abs_time += _Rel_time;
} else {
_Abs_time = _Forever;
}
}
return _Abs_time;
}
namespace this_thread {
_NODISCARD thread::id get_id() noexcept;
inline void yield() noexcept {
_Thrd_yield();
}
inline void sleep_until(const xtime* _Abs_time) {
_Thrd_sleep(_Abs_time);
}
template <class _Clock, class _Duration>
void sleep_until(const chrono::time_point<_Clock, _Duration>& _Abs_time) {
#if _HAS_CXX20
static_assert(chrono::is_clock_v<_Clock>, "Clock type required");
#endif // _HAS_CXX20
for (;;) {
const auto _Now = _Clock::now();
if (_Abs_time <= _Now) {
return;
}
_CSTD xtime _Tgt;
(void) _To_xtime_10_day_clamped(_Tgt, _Abs_time - _Now);
_Thrd_sleep(&_Tgt);
}
}
template <class _Rep, class _Period>
void sleep_for(const chrono::duration<_Rep, _Period>& _Rel_time) {
sleep_until(_To_absolute_time(_Rel_time));
}
} // namespace this_thread
class thread::id { // thread id
public:
id() noexcept : _Id(0) {} // id for no thread
private:
id(_Thrd_id_t _Other_id) : _Id(_Other_id) {}
_Thrd_id_t _Id;
friend thread::id thread::get_id() const noexcept;
friend thread::id this_thread::get_id() noexcept;
friend bool operator==(thread::id _Left, thread::id _Right) noexcept;
#if _HAS_CXX20
friend strong_ordering operator<=>(thread::id _Left, thread::id _Right) noexcept;
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
friend bool operator<(thread::id _Left, thread::id _Right) noexcept;
#endif // !_HAS_CXX20
template <class _Ch, class _Tr>
friend basic_ostream<_Ch, _Tr>& operator<<(basic_ostream<_Ch, _Tr>& _Str, thread::id _Id);
friend hash<thread::id>;
};
_NODISCARD inline thread::id thread::get_id() const noexcept {
return _Thr._Id;
}
_NODISCARD inline thread::id this_thread::get_id() noexcept {
return _Thrd_id();
}
inline void swap(thread& _Left, thread& _Right) noexcept {
_Left.swap(_Right);
}
_NODISCARD inline bool operator==(thread::id _Left, thread::id _Right) noexcept {
return _Left._Id == _Right._Id;
}
#if _HAS_CXX20
_NODISCARD inline strong_ordering operator<=>(thread::id _Left, thread::id _Right) noexcept {
return _Left._Id <=> _Right._Id;
}
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
_NODISCARD inline bool operator!=(thread::id _Left, thread::id _Right) noexcept {
return !(_Left == _Right);
}
_NODISCARD inline bool operator<(thread::id _Left, thread::id _Right) noexcept {
return _Left._Id < _Right._Id;
}
_NODISCARD inline bool operator<=(thread::id _Left, thread::id _Right) noexcept {
return !(_Right < _Left);
}
_NODISCARD inline bool operator>(thread::id _Left, thread::id _Right) noexcept {
return _Right < _Left;
}
_NODISCARD inline bool operator>=(thread::id _Left, thread::id _Right) noexcept {
return !(_Left < _Right);
}
#endif // !_HAS_CXX20
template <class _Ch, class _Tr>
basic_ostream<_Ch, _Tr>& operator<<(basic_ostream<_Ch, _Tr>& _Str, thread::id _Id) {
return _Str << _Id._Id;
}
// STRUCT TEMPLATE SPECIALIZATION hash
template <>
struct hash<thread::id> {
_CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef thread::id _ARGUMENT_TYPE_NAME;
_CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef size_t _RESULT_TYPE_NAME;
_NODISCARD size_t operator()(const thread::id _Keyval) const noexcept {
return _Hash_representation(_Keyval._Id);
}
};
#if _HAS_CXX20
class jthread {
public:
using id = thread::id;
using native_handle_type = thread::native_handle_type;
jthread() noexcept : _Impl{}, _Ssource{nostopstate} {}
template <class _Fn, class... _Args, enable_if_t<!is_same_v<remove_cvref_t<_Fn>, jthread>, int> = 0>
_NODISCARD_CTOR explicit jthread(_Fn&& _Fx, _Args&&... _Ax) {
if constexpr (is_invocable_v<decay_t<_Fn>, stop_token, decay_t<_Args>...>) {
_Impl._Start(_STD forward<_Fn>(_Fx), _Ssource.get_token(), _STD forward<_Args>(_Ax)...);
} else {
_Impl._Start(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...);
}
}
~jthread() {
_Try_cancel_and_join();
}
jthread(const jthread&) = delete;
jthread(jthread&&) noexcept = default;
jthread& operator=(const jthread&) = delete;
jthread& operator=(jthread&& _Other) noexcept {
// note: the standard specifically disallows making self-move-assignment a no-op here
// N4861 [thread.jthread.cons]/13
// Effects: If joinable() is true, calls request_stop() and then join(). Assigns the state
// of x to *this and sets x to a default constructed state.
_Try_cancel_and_join();
_Impl = _STD move(_Other._Impl);
_Ssource = _STD move(_Other._Ssource);
return *this;
}
void swap(jthread& _Other) noexcept {
_Impl.swap(_Other._Impl);
_Ssource.swap(_Other._Ssource);
}
_NODISCARD bool joinable() const noexcept {
return _Impl.joinable();
}
void join() {
_Impl.join();
}
void detach() {
_Impl.detach();
}
_NODISCARD id get_id() const noexcept {
return _Impl.get_id();
}
_NODISCARD stop_source get_stop_source() noexcept {
return _Ssource;
}
_NODISCARD stop_token get_stop_token() const noexcept {
return _Ssource.get_token();
}
bool request_stop() noexcept {
return _Ssource.request_stop();
}
friend void swap(jthread& _Lhs, jthread& _Rhs) noexcept {
_Lhs.swap(_Rhs);
}
_NODISCARD static unsigned int hardware_concurrency() noexcept {
return thread::hardware_concurrency();
}
private:
void _Try_cancel_and_join() noexcept {
if (_Impl.joinable()) {
_Ssource.request_stop();
_Impl.join();
}
}
thread _Impl;
stop_source _Ssource;
};
#endif // _HAS_CXX20
_STD_END
#pragma pop_macro("new")
_STL_RESTORE_CLANG_WARNINGS
#pragma warning(pop)
#pragma pack(pop)
#endif // _STL_COMPILER_PREPROCESSOR
#endif // _THREAD_