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