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>
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 
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 
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 
77   thread() : Thread(native_handle_type()) {}
78   thread(thread &&Other) noexcept
79       : Thread(std::exchange(Other.Thread, native_handle_type())) {}
80 
81   template <class Function, class... 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 
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 
102   bool joinable() const noexcept { return Thread != native_handle_type(); }
103 
104   inline id get_id() const noexcept;
105 
106   native_handle_type native_handle() const noexcept { return Thread; }
107 
108   static unsigned hardware_concurrency() {
109     return std::thread::hardware_concurrency();
110   };
111 
112   inline void join();
113   inline void detach();
114 
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>
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 
144 thread::id thread::get_id() const noexcept {
145   return llvm_thread_get_id_impl(Thread);
146 }
147 
148 void thread::join() {
149   llvm_thread_join_impl(Thread);
150   Thread = native_handle_type();
151 }
152 
153 void thread::detach() {
154   llvm_thread_detach_impl(Thread);
155   Thread = native_handle_type();
156 }
157 
158 namespace this_thread {
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 {
226   thread() {}
227   thread(thread &&other) {}
228   template <class Function, class... Args>
229   explicit thread(llvm::Optional<unsigned> StackSizeInBytes, Function &&f,
230                   Args &&...args) {
231     f(std::forward<Args>(args)...);
232   }
233   template <class Function, class... Args>
234   explicit thread(Function &&f, Args &&...args) {
235     f(std::forward<Args>(args)...);
236   }
237   thread(const thread &) = delete;
238 
239   void detach() {
240     report_fatal_error("Detaching from a thread does not make sense with no "
241                        "threading support");
242   }
243   void join() {}
244   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