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
23typedef unsigned long DWORD;
24typedef void *PVOID;
25typedef PVOID HANDLE;
26#endif
27
28#if LLVM_ENABLE_THREADS
29
30#include <thread>
31
32namespace llvm {
33
34#if LLVM_ON_UNIX || _WIN32
35
36/// LLVM thread following std::thread interface with added constructor to
37/// specify stack size.
38class 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
48public:
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
112private:
113  native_handle_type Thread;
114};
115
116thread::native_handle_type
117llvm_execute_on_thread_impl(thread::start_routine_type ThreadFunc, void *Arg,
118                            std::optional<unsigned> StackSizeInBytes);
119void llvm_thread_join_impl(thread::native_handle_type Thread);
120void llvm_thread_detach_impl(thread::native_handle_type Thread);
121thread::id llvm_thread_get_id_impl(thread::native_handle_type Thread);
122thread::id llvm_thread_get_current_id_impl();
123
124template <class Function, class... Args>
125thread::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
137thread::id thread::get_id() const noexcept {
138  return llvm_thread_get_id_impl(Thread);
139}
140
141void thread::join() {
142  llvm_thread_join_impl(Thread);
143  Thread = native_handle_type();
144}
145
146void thread::detach() {
147  llvm_thread_detach_impl(Thread);
148  Thread = native_handle_type();
149}
150
151namespace this_thread {
152inline 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.
159class thread {
160public:
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
200private:
201  std::thread Thread;
202};
203
204namespace 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
216namespace llvm {
217
218struct 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