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