src/third-party/discord-rpc/include/mingw-std-threads/thread (view raw)
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_next <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 <ostream> // Stream output for thread ids.
39#include <utility> // For std::swap, std::forward
40
41// For the invoke implementation only:
42#include <type_traits> // For std::result_of, etc.
43//#include <utility> // For std::forward
44//#include <functional> // For std::reference_wrapper
45
46#include <windows.h>
47#include <process.h> // For _beginthreadex
48
49#ifndef NDEBUG
50#include <cstdio>
51#endif
52
53#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0501)
54#error To use the MinGW-std-threads library, you will need to define the macro _WIN32_WINNT to be 0x0501 (Windows XP) or higher.
55#endif
56
57// Instead of INVALID_HANDLE_VALUE, _beginthreadex returns 0.
58namespace mingw_stdthread
59{
60namespace detail
61{
62// For compatibility, implement std::invoke for C++11 and C++14
63#if __cplusplus < 201703L
64 template<bool PMemFunc, bool PMemData>
65 struct Invoker
66 {
67 template<class F, class... Args>
68 inline static typename std::result_of<F(Args...)>::type invoke (F&& f, Args&&... args)
69 {
70 return std::forward<F>(f)(std::forward<Args>(args)...);
71 }
72 };
73 template<bool>
74 struct InvokerHelper;
75
76 template<>
77 struct InvokerHelper<false>
78 {
79 template<class T1>
80 inline static auto get (T1&& t1) -> decltype(*std::forward<T1>(t1))
81 {
82 return *std::forward<T1>(t1);
83 }
84
85 template<class T1>
86 inline static auto get (const std::reference_wrapper<T1>& t1) -> decltype(t1.get())
87 {
88 return t1.get();
89 }
90 };
91
92 template<>
93 struct InvokerHelper<true>
94 {
95 template<class T1>
96 inline static auto get (T1&& t1) -> decltype(std::forward<T1>(t1))
97 {
98 return std::forward<T1>(t1);
99 }
100 };
101
102 template<>
103 struct Invoker<true, false>
104 {
105 template<class T, class F, class T1, class... Args>
106 inline static auto invoke (F T::* f, T1&& t1, Args&&... args) ->\
107 decltype((InvokerHelper<std::is_base_of<T,typename std::decay<T1>::type>::value>::get(std::forward<T1>(t1)).*f)(std::forward<Args>(args)...))
108 {
109 return (InvokerHelper<std::is_base_of<T,typename std::decay<T1>::type>::value>::get(std::forward<T1>(t1)).*f)(std::forward<Args>(args)...);
110 }
111 };
112
113 template<>
114 struct Invoker<false, true>
115 {
116 template<class T, class F, class T1, class... Args>
117 inline static auto invoke (F T::* f, T1&& t1, Args&&... args) ->\
118 decltype(InvokerHelper<std::is_base_of<T,typename std::decay<T1>::type>::value>::get(t1).*f)
119 {
120 return InvokerHelper<std::is_base_of<T,typename std::decay<T1>::type>::value>::get(t1).*f;
121 }
122 };
123
124 template<class F, class... Args>
125 struct InvokeResult
126 {
127 typedef Invoker<std::is_member_function_pointer<typename std::remove_reference<F>::type>::value,
128 std::is_member_object_pointer<typename std::remove_reference<F>::type>::value &&
129 (sizeof...(Args) == 1)> invoker;
130 inline static auto invoke (F&& f, Args&&... args) -> decltype(invoker::invoke(std::forward<F>(f), std::forward<Args>(args)...))
131 {
132 return invoker::invoke(std::forward<F>(f), std::forward<Args>(args)...);
133 };
134 };
135
136 template<class F, class...Args>
137 auto invoke (F&& f, Args&&... args) -> decltype(InvokeResult<F, Args...>::invoke(std::forward<F>(f), std::forward<Args>(args)...))
138 {
139 return InvokeResult<F, Args...>::invoke(std::forward<F>(f), std::forward<Args>(args)...);
140 }
141#else
142 using std::invoke;
143#endif
144
145 template<std::size_t...>
146 struct IntSeq {};
147
148 template<std::size_t N, std::size_t... S>
149 struct GenIntSeq : GenIntSeq<N-1, N-1, S...> { };
150
151 template<std::size_t... S>
152 struct GenIntSeq<0, S...> { typedef IntSeq<S...> type; };
153
154 // We can't define the Call struct in the function - the standard forbids template methods in that case
155 template<class Func, typename... Args>
156 class ThreadFuncCall
157 {
158 typedef std::tuple<Args...> Tuple;
159 Func mFunc;
160 Tuple mArgs;
161
162 template <std::size_t... S>
163 void callFunc(detail::IntSeq<S...>)
164 {
165 detail::invoke(std::forward<Func>(mFunc), std::get<S>(std::forward<Tuple>(mArgs)) ...);
166 }
167 public:
168 ThreadFuncCall(Func&& aFunc, Args&&... aArgs)
169 :mFunc(std::forward<Func>(aFunc)), mArgs(std::forward<Args>(aArgs)...){}
170
171 void callFunc()
172 {
173 callFunc(typename detail::GenIntSeq<sizeof...(Args)>::type());
174 }
175 };
176
177} // Namespace "detail"
178
179class thread
180{
181public:
182 class id
183 {
184 DWORD mId;
185 void clear() {mId = 0;}
186 friend class thread;
187 friend class std::hash<id>;
188 public:
189 explicit id(DWORD aId=0) noexcept : mId(aId){}
190 friend bool operator==(id x, id y) noexcept {return x.mId == y.mId; }
191 friend bool operator!=(id x, id y) noexcept {return x.mId != y.mId; }
192 friend bool operator< (id x, id y) noexcept {return x.mId < y.mId; }
193 friend bool operator<=(id x, id y) noexcept {return x.mId <= y.mId; }
194 friend bool operator> (id x, id y) noexcept {return x.mId > y.mId; }
195 friend bool operator>=(id x, id y) noexcept {return x.mId >= y.mId; }
196
197 template<class _CharT, class _Traits>
198 friend std::basic_ostream<_CharT, _Traits>&
199 operator<<(std::basic_ostream<_CharT, _Traits>& __out, id __id)
200 {
201 if (__id.mId == 0)
202 {
203 return __out << "(invalid std::thread::id)";
204 }
205 else
206 {
207 return __out << __id.mId;
208 }
209 }
210 };
211private:
212 static constexpr HANDLE kInvalidHandle = nullptr;
213 HANDLE mHandle;
214 id mThreadId;
215
216 template <class Call>
217 static unsigned __stdcall threadfunc(void* arg)
218 {
219 std::unique_ptr<Call> call(static_cast<Call*>(arg));
220 call->callFunc();
221 return 0;
222 }
223
224 static unsigned int _hardware_concurrency_helper() noexcept
225 {
226 SYSTEM_INFO sysinfo;
227// This is one of the few functions used by the library which has a nearly-
228// equivalent function defined in earlier versions of Windows. Include the
229// workaround, just as a reminder that it does exist.
230#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501)
231 ::GetNativeSystemInfo(&sysinfo);
232#else
233 ::GetSystemInfo(&sysinfo);
234#endif
235 return sysinfo.dwNumberOfProcessors;
236 }
237public:
238 typedef HANDLE native_handle_type;
239 id get_id() const noexcept {return mThreadId;}
240 native_handle_type native_handle() const {return mHandle;}
241 thread(): mHandle(kInvalidHandle), mThreadId(){}
242
243 thread(thread&& other)
244 :mHandle(other.mHandle), mThreadId(other.mThreadId)
245 {
246 other.mHandle = kInvalidHandle;
247 other.mThreadId.clear();
248 }
249
250 thread(const thread &other)=delete;
251
252 template<class Func, typename... Args>
253 explicit thread(Func&& func, Args&&... args) : mHandle(), mThreadId()
254 {
255 typedef detail::ThreadFuncCall<Func, Args...> Call;
256 auto call = new Call(
257 std::forward<Func>(func), std::forward<Args>(args)...);
258 auto int_handle = _beginthreadex(NULL, 0, threadfunc<Call>,
259 static_cast<LPVOID>(call), 0,
260 reinterpret_cast<unsigned*>(&(mThreadId.mId)));
261 if (int_handle == 0)
262 {
263 mHandle = kInvalidHandle;
264 int errnum = errno;
265 delete call;
266// Note: Should only throw EINVAL, EAGAIN, EACCES
267 throw std::system_error(errnum, std::generic_category());
268 } else
269 mHandle = reinterpret_cast<HANDLE>(int_handle);
270 }
271
272 bool joinable() const {return mHandle != kInvalidHandle;}
273
274// Note: Due to lack of synchronization, this function has a race condition
275// if called concurrently, which leads to undefined behavior. The same applies
276// to all other member functions of this class, but this one is mentioned
277// explicitly.
278 void join()
279 {
280 using namespace std;
281 if (get_id() == id(GetCurrentThreadId()))
282 throw system_error(make_error_code(errc::resource_deadlock_would_occur));
283 if (mHandle == kInvalidHandle)
284 throw system_error(make_error_code(errc::no_such_process));
285 if (!joinable())
286 throw system_error(make_error_code(errc::invalid_argument));
287 WaitForSingleObject(mHandle, INFINITE);
288 CloseHandle(mHandle);
289 mHandle = kInvalidHandle;
290 mThreadId.clear();
291 }
292
293 ~thread()
294 {
295 if (joinable())
296 {
297#ifndef NDEBUG
298 std::printf("Error: Must join() or detach() a thread before \
299destroying it.\n");
300#endif
301 std::terminate();
302 }
303 }
304 thread& operator=(const thread&) = delete;
305 thread& operator=(thread&& other) noexcept
306 {
307 if (joinable())
308 {
309#ifndef NDEBUG
310 std::printf("Error: Must join() or detach() a thread before \
311moving another thread to it.\n");
312#endif
313 std::terminate();
314 }
315 swap(std::forward<thread>(other));
316 return *this;
317 }
318 void swap(thread&& other) noexcept
319 {
320 std::swap(mHandle, other.mHandle);
321 std::swap(mThreadId.mId, other.mThreadId.mId);
322 }
323
324 static unsigned int hardware_concurrency() noexcept
325 {
326 static unsigned int cached = _hardware_concurrency_helper();
327 return cached;
328 }
329
330 void detach()
331 {
332 if (!joinable())
333 {
334 using namespace std;
335 throw system_error(make_error_code(errc::invalid_argument));
336 }
337 if (mHandle != kInvalidHandle)
338 {
339 CloseHandle(mHandle);
340 mHandle = kInvalidHandle;
341 }
342 mThreadId.clear();
343 }
344};
345
346namespace this_thread
347{
348 inline thread::id get_id() noexcept {return thread::id(GetCurrentThreadId());}
349 inline void yield() noexcept {Sleep(0);}
350 template< class Rep, class Period >
351 void sleep_for( const std::chrono::duration<Rep,Period>& sleep_duration)
352 {
353 using namespace std::chrono;
354 using rep = milliseconds::rep;
355 rep ms = duration_cast<milliseconds>(sleep_duration).count();
356 while (ms > 0)
357 {
358 constexpr rep kMaxRep = static_cast<rep>(INFINITE - 1);
359 auto sleepTime = (ms < kMaxRep) ? ms : kMaxRep;
360 Sleep(static_cast<DWORD>(sleepTime));
361 ms -= sleepTime;
362 }
363 }
364 template <class Clock, class Duration>
365 void sleep_until(const std::chrono::time_point<Clock,Duration>& sleep_time)
366 {
367 sleep_for(sleep_time-Clock::now());
368 }
369}
370} // Namespace mingw_stdthread
371
372namespace std
373{
374// Because of quirks of the compiler, the common "using namespace std;"
375// directive would flatten the namespaces and introduce ambiguity where there
376// was none. Direct specification (std::), however, would be unaffected.
377// Take the safe option, and include only in the presence of MinGW's win32
378// implementation.
379#if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS)
380using mingw_stdthread::thread;
381// Remove ambiguity immediately, to avoid problems arising from the above.
382//using std::thread;
383namespace this_thread
384{
385using namespace mingw_stdthread::this_thread;
386}
387#elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING) // Skip repetition
388#define MINGW_STDTHREAD_REDUNDANCY_WARNING
389#pragma message "This version of MinGW seems to include a win32 port of\
390 pthreads, and probably already has C++11 std threading classes implemented,\
391 based on pthreads. These classes, found in namespace std, are not overridden\
392 by the mingw-std-thread library. If you would still like to use this\
393 implementation (as it is more lightweight), use the classes provided in\
394 namespace mingw_stdthread."
395#endif
396
397// Specialize hash for this implementation's thread::id, even if the
398// std::thread::id already has a hash.
399template<>
400struct hash<mingw_stdthread::thread::id>
401{
402 typedef mingw_stdthread::thread::id argument_type;
403 typedef size_t result_type;
404 size_t operator() (const argument_type & i) const noexcept
405 {
406 return i.mId;
407 }
408};
409}
410#endif // WIN32STDTHREAD_H