1 //===-- llvm/Support/thread.h - Wrapper for <thread> ------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This header is a wrapper for <thread> that works around problems with the
10 // MSVC headers when exceptions are disabled. It also provides llvm::thread,
11 // which is either a typedef of std::thread or a replacement that calls the
12 // function synchronously depending on the value of LLVM_ENABLE_THREADS.
13 //
14 //===----------------------------------------------------------------------===//
15
16 #ifndef LLVM_SUPPORT_THREAD_H
17 #define LLVM_SUPPORT_THREAD_H
18
19 #include "llvm/ADT/Optional.h"
20 #include "llvm/Config/llvm-config.h"
21
22 #ifdef _WIN32
23 typedef unsigned long DWORD;
24 typedef void *PVOID;
25 typedef PVOID HANDLE;
26 #endif
27
28 #if LLVM_ENABLE_THREADS
29
30 #include <thread>
31
32 namespace llvm {
33
34 #if LLVM_ON_UNIX || _WIN32
35
36 /// LLVM thread following std::thread interface with added constructor to
37 /// specify stack size.
38 class thread {
39 template <typename FPtr, typename... Args, size_t... Indices>
Apply(std::tuple<FPtr,Args...> & Callee,std::index_sequence<Indices...>)40 static void Apply(std::tuple<FPtr, Args...> &Callee,
41 std::index_sequence<Indices...>) {
42 std::move(std::get<0>(Callee))(std::move(std::get<Indices + 1>(Callee))...);
43 }
44
GenericThreadProxy(void * Ptr)45 template <typename CalleeTuple> static void GenericThreadProxy(void *Ptr) {
46 std::unique_ptr<CalleeTuple> Callee(static_cast<CalleeTuple *>(Ptr));
47
48 // FIXME: use std::apply when C++17 is allowed.
49 std::make_index_sequence<std::tuple_size<CalleeTuple>() - 1> Indices{};
50 Apply(*Callee.get(), Indices);
51 }
52
53 public:
54 #if LLVM_ON_UNIX
55 using native_handle_type = pthread_t;
56 using id = pthread_t;
57 using start_routine_type = void *(*)(void *);
58
ThreadProxy(void * Ptr)59 template <typename CalleeTuple> static void *ThreadProxy(void *Ptr) {
60 GenericThreadProxy<CalleeTuple>(Ptr);
61 return nullptr;
62 }
63 #elif _WIN32
64 using native_handle_type = HANDLE;
65 using id = DWORD;
66 using start_routine_type = unsigned(__stdcall *)(void *);
67
68 template <typename CalleeTuple>
69 static unsigned __stdcall ThreadProxy(void *Ptr) {
70 GenericThreadProxy<CalleeTuple>(Ptr);
71 return 0;
72 }
73 #endif
74
75 static const llvm::Optional<unsigned> DefaultStackSize;
76
thread()77 thread() : Thread(native_handle_type()) {}
thread(thread && Other)78 thread(thread &&Other) noexcept
79 : Thread(std::exchange(Other.Thread, native_handle_type())) {}
80
81 template <class Function, class... Args>
thread(Function && f,Args &&...args)82 explicit thread(Function &&f, Args &&...args)
83 : thread(DefaultStackSize, f, args...) {}
84
85 template <class Function, class... Args>
86 explicit thread(llvm::Optional<unsigned> StackSizeInBytes, Function &&f,
87 Args &&...args);
88 thread(const thread &) = delete;
89
~thread()90 ~thread() {
91 if (joinable())
92 std::terminate();
93 }
94
95 thread &operator=(thread &&Other) noexcept {
96 if (joinable())
97 std::terminate();
98 Thread = std::exchange(Other.Thread, native_handle_type());
99 return *this;
100 }
101
joinable()102 bool joinable() const noexcept { return Thread != native_handle_type(); }
103
104 inline id get_id() const noexcept;
105
native_handle()106 native_handle_type native_handle() const noexcept { return Thread; }
107
hardware_concurrency()108 static unsigned hardware_concurrency() {
109 return std::thread::hardware_concurrency();
110 };
111
112 inline void join();
113 inline void detach();
114
swap(llvm::thread & Other)115 void swap(llvm::thread &Other) noexcept { std::swap(Thread, Other.Thread); }
116
117 private:
118 native_handle_type Thread;
119 };
120
121 thread::native_handle_type
122 llvm_execute_on_thread_impl(thread::start_routine_type ThreadFunc, void *Arg,
123 llvm::Optional<unsigned> StackSizeInBytes);
124 void llvm_thread_join_impl(thread::native_handle_type Thread);
125 void llvm_thread_detach_impl(thread::native_handle_type Thread);
126 thread::id llvm_thread_get_id_impl(thread::native_handle_type Thread);
127 thread::id llvm_thread_get_current_id_impl();
128
129 template <class Function, class... Args>
thread(llvm::Optional<unsigned> StackSizeInBytes,Function && f,Args &&...args)130 thread::thread(llvm::Optional<unsigned> StackSizeInBytes, Function &&f,
131 Args &&...args) {
132 typedef std::tuple<typename std::decay<Function>::type,
133 typename std::decay<Args>::type...>
134 CalleeTuple;
135 std::unique_ptr<CalleeTuple> Callee(
136 new CalleeTuple(std::forward<Function>(f), std::forward<Args>(args)...));
137
138 Thread = llvm_execute_on_thread_impl(ThreadProxy<CalleeTuple>, Callee.get(),
139 StackSizeInBytes);
140 if (Thread != native_handle_type())
141 Callee.release();
142 }
143
get_id()144 thread::id thread::get_id() const noexcept {
145 return llvm_thread_get_id_impl(Thread);
146 }
147
join()148 void thread::join() {
149 llvm_thread_join_impl(Thread);
150 Thread = native_handle_type();
151 }
152
detach()153 void thread::detach() {
154 llvm_thread_detach_impl(Thread);
155 Thread = native_handle_type();
156 }
157
158 namespace this_thread {
get_id()159 inline thread::id get_id() { return llvm_thread_get_current_id_impl(); }
160 } // namespace this_thread
161
162 #else // !LLVM_ON_UNIX && !_WIN32
163
164 /// std::thread backed implementation of llvm::thread interface that ignores the
165 /// stack size request.
166 class thread {
167 public:
168 using native_handle_type = std::thread::native_handle_type;
169 using id = std::thread::id;
170
171 thread() : Thread(std::thread()) {}
172 thread(thread &&Other) noexcept
173 : Thread(std::exchange(Other.Thread, std::thread())) {}
174
175 template <class Function, class... Args>
176 explicit thread(llvm::Optional<unsigned> StackSizeInBytes, Function &&f,
177 Args &&...args)
178 : Thread(std::forward<Function>(f), std::forward<Args>(args)...) {}
179
180 template <class Function, class... Args>
181 explicit thread(Function &&f, Args &&...args) : Thread(f, args...) {}
182
183 thread(const thread &) = delete;
184
185 ~thread() {}
186
187 thread &operator=(thread &&Other) noexcept {
188 Thread = std::exchange(Other.Thread, std::thread());
189 return *this;
190 }
191
192 bool joinable() const noexcept { return Thread.joinable(); }
193
194 id get_id() const noexcept { return Thread.get_id(); }
195
196 native_handle_type native_handle() noexcept { return Thread.native_handle(); }
197
198 static unsigned hardware_concurrency() {
199 return std::thread::hardware_concurrency();
200 };
201
202 inline void join() { Thread.join(); }
203 inline void detach() { Thread.detach(); }
204
205 void swap(llvm::thread &Other) noexcept { std::swap(Thread, Other.Thread); }
206
207 private:
208 std::thread Thread;
209 };
210
211 namespace this_thread {
212 inline thread::id get_id() { return std::this_thread::get_id(); }
213 }
214
215 #endif // LLVM_ON_UNIX || _WIN32
216
217 } // namespace llvm
218
219 #else // !LLVM_ENABLE_THREADS
220
221 #include <utility>
222
223 namespace llvm {
224
225 struct thread {
threadthread226 thread() {}
threadthread227 thread(thread &&other) {}
228 template <class Function, class... Args>
threadthread229 explicit thread(llvm::Optional<unsigned> StackSizeInBytes, Function &&f,
230 Args &&...args) {
231 f(std::forward<Args>(args)...);
232 }
233 template <class Function, class... Args>
threadthread234 explicit thread(Function &&f, Args &&...args) {
235 f(std::forward<Args>(args)...);
236 }
237 thread(const thread &) = delete;
238
detachthread239 void detach() {
240 report_fatal_error("Detaching from a thread does not make sense with no "
241 "threading support");
242 }
jointhread243 void join() {}
hardware_concurrencythread244 static unsigned hardware_concurrency() { return 1; };
245 };
246
247 } // namespace llvm
248
249 #endif // LLVM_ENABLE_THREADS
250
251 #endif // LLVM_SUPPORT_THREAD_H
252