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/Config/llvm-config.h"
20 #include <optional>
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 CalleeTuple> static void GenericThreadProxy(void *Ptr) {
40     std::unique_ptr<CalleeTuple> Callee(static_cast<CalleeTuple *>(Ptr));
41     std::apply(
42         [](auto &&F, auto &&...Args) {
43           std::forward<decltype(F)>(F)(std::forward<decltype(Args)>(Args)...);
44         },
45         *Callee);
46   }
47 
48 public:
49 #if LLVM_ON_UNIX
50   using native_handle_type = pthread_t;
51   using id = pthread_t;
52   using start_routine_type = void *(*)(void *);
53 
54   template <typename CalleeTuple> static void *ThreadProxy(void *Ptr) {
55     GenericThreadProxy<CalleeTuple>(Ptr);
56     return nullptr;
57   }
58 #elif _WIN32
59   using native_handle_type = HANDLE;
60   using id = DWORD;
61   using start_routine_type = unsigned(__stdcall *)(void *);
62 
63   template <typename CalleeTuple>
64   static unsigned __stdcall ThreadProxy(void *Ptr) {
65     GenericThreadProxy<CalleeTuple>(Ptr);
66     return 0;
67   }
68 #endif
69 
70   static const std::optional<unsigned> DefaultStackSize;
71 
72   thread() : Thread(native_handle_type()) {}
73   thread(thread &&Other) noexcept
74       : Thread(std::exchange(Other.Thread, native_handle_type())) {}
75 
76   template <class Function, class... Args>
77   explicit thread(Function &&f, Args &&...args)
78       : thread(DefaultStackSize, f, args...) {}
79 
80   template <class Function, class... Args>
81   explicit thread(std::optional<unsigned> StackSizeInBytes, Function &&f,
82                   Args &&...args);
83   thread(const thread &) = delete;
84 
85   ~thread() {
86     if (joinable())
87       std::terminate();
88   }
89 
90   thread &operator=(thread &&Other) noexcept {
91     if (joinable())
92       std::terminate();
93     Thread = std::exchange(Other.Thread, native_handle_type());
94     return *this;
95   }
96 
97   bool joinable() const noexcept { return Thread != native_handle_type(); }
98 
99   inline id get_id() const noexcept;
100 
101   native_handle_type native_handle() const noexcept { return Thread; }
102 
103   static unsigned hardware_concurrency() {
104     return std::thread::hardware_concurrency();
105   };
106 
107   inline void join();
108   inline void detach();
109 
110   void swap(llvm::thread &Other) noexcept { std::swap(Thread, Other.Thread); }
111 
112 private:
113   native_handle_type Thread;
114 };
115 
116 thread::native_handle_type
117 llvm_execute_on_thread_impl(thread::start_routine_type ThreadFunc, void *Arg,
118                             std::optional<unsigned> StackSizeInBytes);
119 void llvm_thread_join_impl(thread::native_handle_type Thread);
120 void llvm_thread_detach_impl(thread::native_handle_type Thread);
121 thread::id llvm_thread_get_id_impl(thread::native_handle_type Thread);
122 thread::id llvm_thread_get_current_id_impl();
123 
124 template <class Function, class... Args>
125 thread::thread(std::optional<unsigned> StackSizeInBytes, Function &&f,
126                Args &&...args) {
127   typedef std::tuple<std::decay_t<Function>, std::decay_t<Args>...> CalleeTuple;
128   std::unique_ptr<CalleeTuple> Callee(
129       new CalleeTuple(std::forward<Function>(f), std::forward<Args>(args)...));
130 
131   Thread = llvm_execute_on_thread_impl(ThreadProxy<CalleeTuple>, Callee.get(),
132                                        StackSizeInBytes);
133   if (Thread != native_handle_type())
134     Callee.release();
135 }
136 
137 thread::id thread::get_id() const noexcept {
138   return llvm_thread_get_id_impl(Thread);
139 }
140 
141 void thread::join() {
142   llvm_thread_join_impl(Thread);
143   Thread = native_handle_type();
144 }
145 
146 void thread::detach() {
147   llvm_thread_detach_impl(Thread);
148   Thread = native_handle_type();
149 }
150 
151 namespace this_thread {
152 inline thread::id get_id() { return llvm_thread_get_current_id_impl(); }
153 } // namespace this_thread
154 
155 #else // !LLVM_ON_UNIX && !_WIN32
156 
157 /// std::thread backed implementation of llvm::thread interface that ignores the
158 /// stack size request.
159 class thread {
160 public:
161   using native_handle_type = std::thread::native_handle_type;
162   using id = std::thread::id;
163 
164   thread() : Thread(std::thread()) {}
165   thread(thread &&Other) noexcept
166       : Thread(std::exchange(Other.Thread, std::thread())) {}
167 
168   template <class Function, class... Args>
169   explicit thread(std::optional<unsigned> StackSizeInBytes, Function &&f,
170                   Args &&...args)
171       : Thread(std::forward<Function>(f), std::forward<Args>(args)...) {}
172 
173   template <class Function, class... Args>
174   explicit thread(Function &&f, Args &&...args) : Thread(f, args...) {}
175 
176   thread(const thread &) = delete;
177 
178   ~thread() {}
179 
180   thread &operator=(thread &&Other) noexcept {
181     Thread = std::exchange(Other.Thread, std::thread());
182     return *this;
183   }
184 
185   bool joinable() const noexcept { return Thread.joinable(); }
186 
187   id get_id() const noexcept { return Thread.get_id(); }
188 
189   native_handle_type native_handle() noexcept { return Thread.native_handle(); }
190 
191   static unsigned hardware_concurrency() {
192     return std::thread::hardware_concurrency();
193   };
194 
195   inline void join() { Thread.join(); }
196   inline void detach() { Thread.detach(); }
197 
198   void swap(llvm::thread &Other) noexcept { std::swap(Thread, Other.Thread); }
199 
200 private:
201   std::thread Thread;
202 };
203 
204 namespace this_thread {
205   inline thread::id get_id() { return std::this_thread::get_id(); }
206 }
207 
208 #endif // LLVM_ON_UNIX || _WIN32
209 
210 } // namespace llvm
211 
212 #else // !LLVM_ENABLE_THREADS
213 
214 #include <utility>
215 
216 namespace llvm {
217 
218 struct thread {
219   thread() {}
220   thread(thread &&other) {}
221   template <class Function, class... Args>
222   explicit thread(std::optional<unsigned> StackSizeInBytes, Function &&f,
223                   Args &&...args) {
224     f(std::forward<Args>(args)...);
225   }
226   template <class Function, class... Args>
227   explicit thread(Function &&f, Args &&...args) {
228     f(std::forward<Args>(args)...);
229   }
230   thread(const thread &) = delete;
231 
232   void detach() {
233     report_fatal_error("Detaching from a thread does not make sense with no "
234                        "threading support");
235   }
236   void join() {}
237   static unsigned hardware_concurrency() { return 1; };
238 };
239 
240 } // namespace llvm
241 
242 #endif // LLVM_ENABLE_THREADS
243 
244 #endif // LLVM_SUPPORT_THREAD_H
245