xref: /freebsd/contrib/llvm-project/llvm/lib/Support/Unix/Threading.inc (revision e6bfd18d21b225af6a0ed67ceeaf1293b7b9eba5)
1//===- Unix/Threading.inc - Unix Threading Implementation ----- -*- 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 file provides the Unix specific implementation of Threading functions.
10//
11//===----------------------------------------------------------------------===//
12
13#include "Unix.h"
14#include "llvm/ADT/ScopeExit.h"
15#include "llvm/ADT/SmallString.h"
16#include "llvm/ADT/Twine.h"
17
18#if defined(__APPLE__)
19#include <mach/mach_init.h>
20#include <mach/mach_port.h>
21#include <pthread/qos.h>
22#endif
23
24#include <pthread.h>
25
26#if defined(__FreeBSD__) || defined(__OpenBSD__)
27#include <pthread_np.h> // For pthread_getthreadid_np() / pthread_set_name_np()
28#endif
29
30#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
31#include <errno.h>
32#include <sys/cpuset.h>
33#include <sys/sysctl.h>
34#include <sys/user.h>
35#include <unistd.h>
36#endif
37
38#if defined(__NetBSD__)
39#include <lwp.h> // For _lwp_self()
40#endif
41
42#if defined(__OpenBSD__)
43#include <unistd.h> // For getthrid()
44#endif
45
46#if defined(__linux__)
47#include <sched.h>       // For sched_getaffinity
48#include <sys/syscall.h> // For syscall codes
49#include <unistd.h>      // For syscall()
50#endif
51
52namespace llvm {
53pthread_t
54llvm_execute_on_thread_impl(void *(*ThreadFunc)(void *), void *Arg,
55                            llvm::Optional<unsigned> StackSizeInBytes) {
56  int errnum;
57
58  // Construct the attributes object.
59  pthread_attr_t Attr;
60  if ((errnum = ::pthread_attr_init(&Attr)) != 0) {
61    ReportErrnumFatal("pthread_attr_init failed", errnum);
62  }
63
64  auto AttrGuard = llvm::make_scope_exit([&] {
65    if ((errnum = ::pthread_attr_destroy(&Attr)) != 0) {
66      ReportErrnumFatal("pthread_attr_destroy failed", errnum);
67    }
68  });
69
70  // Set the requested stack size, if given.
71  if (StackSizeInBytes) {
72    if ((errnum = ::pthread_attr_setstacksize(&Attr, *StackSizeInBytes)) != 0) {
73      ReportErrnumFatal("pthread_attr_setstacksize failed", errnum);
74    }
75  }
76
77  // Construct and execute the thread.
78  pthread_t Thread;
79  if ((errnum = ::pthread_create(&Thread, &Attr, ThreadFunc, Arg)) != 0)
80    ReportErrnumFatal("pthread_create failed", errnum);
81
82  return Thread;
83}
84
85void llvm_thread_detach_impl(pthread_t Thread) {
86  int errnum;
87
88  if ((errnum = ::pthread_detach(Thread)) != 0) {
89    ReportErrnumFatal("pthread_detach failed", errnum);
90  }
91}
92
93void llvm_thread_join_impl(pthread_t Thread) {
94  int errnum;
95
96  if ((errnum = ::pthread_join(Thread, nullptr)) != 0) {
97    ReportErrnumFatal("pthread_join failed", errnum);
98  }
99}
100
101pthread_t llvm_thread_get_id_impl(pthread_t Thread) {
102  return Thread;
103}
104
105pthread_t llvm_thread_get_current_id_impl() {
106  return ::pthread_self();
107}
108
109} // namespace llvm
110
111uint64_t llvm::get_threadid() {
112#if defined(__APPLE__)
113  // Calling "mach_thread_self()" bumps the reference count on the thread
114  // port, so we need to deallocate it. mach_task_self() doesn't bump the ref
115  // count.
116  thread_port_t Self = mach_thread_self();
117  mach_port_deallocate(mach_task_self(), Self);
118  return Self;
119#elif defined(__FreeBSD__)
120  return uint64_t(pthread_getthreadid_np());
121#elif defined(__NetBSD__)
122  return uint64_t(_lwp_self());
123#elif defined(__OpenBSD__)
124  return uint64_t(getthrid());
125#elif defined(__ANDROID__)
126  return uint64_t(gettid());
127#elif defined(__linux__)
128  return uint64_t(syscall(SYS_gettid));
129#else
130  return uint64_t(pthread_self());
131#endif
132}
133
134
135static constexpr uint32_t get_max_thread_name_length_impl() {
136#if defined(__NetBSD__)
137  return PTHREAD_MAX_NAMELEN_NP;
138#elif defined(__APPLE__)
139  return 64;
140#elif defined(__linux__)
141#if HAVE_PTHREAD_SETNAME_NP
142  return 16;
143#else
144  return 0;
145#endif
146#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
147  return 16;
148#elif defined(__OpenBSD__)
149  return 32;
150#else
151  return 0;
152#endif
153}
154
155uint32_t llvm::get_max_thread_name_length() {
156  return get_max_thread_name_length_impl();
157}
158
159void llvm::set_thread_name(const Twine &Name) {
160  // Make sure the input is null terminated.
161  SmallString<64> Storage;
162  StringRef NameStr = Name.toNullTerminatedStringRef(Storage);
163
164  // Truncate from the beginning, not the end, if the specified name is too
165  // long.  For one, this ensures that the resulting string is still null
166  // terminated, but additionally the end of a long thread name will usually
167  // be more unique than the beginning, since a common pattern is for similar
168  // threads to share a common prefix.
169  // Note that the name length includes the null terminator.
170  if (get_max_thread_name_length() > 0)
171    NameStr = NameStr.take_back(get_max_thread_name_length() - 1);
172  (void)NameStr;
173#if defined(__linux__)
174#if (defined(__GLIBC__) && defined(_GNU_SOURCE)) || defined(__ANDROID__)
175#if HAVE_PTHREAD_SETNAME_NP
176  ::pthread_setname_np(::pthread_self(), NameStr.data());
177#endif
178#endif
179#elif defined(__FreeBSD__) || defined(__OpenBSD__)
180  ::pthread_set_name_np(::pthread_self(), NameStr.data());
181#elif defined(__NetBSD__)
182  ::pthread_setname_np(::pthread_self(), "%s",
183    const_cast<char *>(NameStr.data()));
184#elif defined(__APPLE__)
185  ::pthread_setname_np(NameStr.data());
186#endif
187}
188
189void llvm::get_thread_name(SmallVectorImpl<char> &Name) {
190  Name.clear();
191
192#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
193  int pid = ::getpid();
194  uint64_t tid = get_threadid();
195
196  struct kinfo_proc *kp = nullptr, *nkp;
197  size_t len = 0;
198  int error;
199  int ctl[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID | KERN_PROC_INC_THREAD,
200    (int)pid };
201
202  while (1) {
203    error = sysctl(ctl, 4, kp, &len, nullptr, 0);
204    if (kp == nullptr || (error != 0 && errno == ENOMEM)) {
205      // Add extra space in case threads are added before next call.
206      len += sizeof(*kp) + len / 10;
207      nkp = (struct kinfo_proc *)::realloc(kp, len);
208      if (nkp == nullptr) {
209        free(kp);
210        return;
211      }
212      kp = nkp;
213      continue;
214    }
215    if (error != 0)
216      len = 0;
217    break;
218  }
219
220  for (size_t i = 0; i < len / sizeof(*kp); i++) {
221    if (kp[i].ki_tid == (lwpid_t)tid) {
222      Name.append(kp[i].ki_tdname, kp[i].ki_tdname + strlen(kp[i].ki_tdname));
223      break;
224    }
225  }
226  free(kp);
227  return;
228#elif defined(__NetBSD__)
229  constexpr uint32_t len = get_max_thread_name_length_impl();
230  char buf[len];
231  ::pthread_getname_np(::pthread_self(), buf, len);
232
233  Name.append(buf, buf + strlen(buf));
234#elif defined(__OpenBSD__)
235  constexpr uint32_t len = get_max_thread_name_length_impl();
236  char buf[len];
237  ::pthread_get_name_np(::pthread_self(), buf, len);
238
239  Name.append(buf, buf + strlen(buf));
240#elif defined(__linux__)
241#if HAVE_PTHREAD_GETNAME_NP
242  constexpr uint32_t len = get_max_thread_name_length_impl();
243  char Buffer[len] = {'\0'};  // FIXME: working around MSan false positive.
244  if (0 == ::pthread_getname_np(::pthread_self(), Buffer, len))
245    Name.append(Buffer, Buffer + strlen(Buffer));
246#endif
247#endif
248}
249
250SetThreadPriorityResult llvm::set_thread_priority(ThreadPriority Priority) {
251#if defined(__linux__) && defined(SCHED_IDLE)
252  // Some *really* old glibcs are missing SCHED_IDLE.
253  // http://man7.org/linux/man-pages/man3/pthread_setschedparam.3.html
254  // http://man7.org/linux/man-pages/man2/sched_setscheduler.2.html
255  sched_param priority;
256  // For each of the above policies, param->sched_priority must be 0.
257  priority.sched_priority = 0;
258  // SCHED_IDLE    for running very low priority background jobs.
259  // SCHED_OTHER   the standard round-robin time-sharing policy;
260  return !pthread_setschedparam(
261             pthread_self(),
262             // FIXME: consider SCHED_BATCH for Low
263             Priority == ThreadPriority::Default ? SCHED_OTHER : SCHED_IDLE,
264             &priority)
265             ? SetThreadPriorityResult::SUCCESS
266             : SetThreadPriorityResult::FAILURE;
267#elif defined(__APPLE__)
268  // https://developer.apple.com/documentation/apple-silicon/tuning-your-code-s-performance-for-apple-silicon
269  //
270  // Background - Applies to work that isn’t visible to the user and may take significant
271  // time to complete. Examples include indexing, backing up, or synchronizing data. This
272  // class emphasizes energy efficiency.
273  //
274  // Utility - Applies to work that takes anywhere from a few seconds to a few minutes to
275  // complete. Examples include downloading a document or importing data. This class
276  // offers a balance between responsiveness, performance, and energy efficiency.
277  const auto qosClass = [&](){
278    switch (Priority) {
279      case ThreadPriority::Background: return QOS_CLASS_BACKGROUND;
280      case ThreadPriority::Low: return QOS_CLASS_UTILITY;
281      case ThreadPriority::Default: return QOS_CLASS_DEFAULT;
282    }
283  }();
284  return !pthread_set_qos_class_self_np(qosClass, 0)
285             ? SetThreadPriorityResult::SUCCESS
286             : SetThreadPriorityResult::FAILURE;
287#endif
288  return SetThreadPriorityResult::FAILURE;
289}
290
291#include <thread>
292
293int computeHostNumHardwareThreads() {
294#if defined(__FreeBSD__)
295  cpuset_t mask;
296  CPU_ZERO(&mask);
297  if (cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, -1, sizeof(mask),
298                         &mask) == 0)
299    return CPU_COUNT(&mask);
300#elif defined(__linux__)
301  cpu_set_t Set;
302  if (sched_getaffinity(0, sizeof(Set), &Set) == 0)
303    return CPU_COUNT(&Set);
304#endif
305  // Guard against std::thread::hardware_concurrency() returning 0.
306  if (unsigned Val = std::thread::hardware_concurrency())
307    return Val;
308  return 1;
309}
310
311void llvm::ThreadPoolStrategy::apply_thread_strategy(
312    unsigned ThreadPoolNum) const {}
313
314llvm::BitVector llvm::get_thread_affinity_mask() {
315  // FIXME: Implement
316  llvm_unreachable("Not implemented!");
317}
318
319unsigned llvm::get_cpus() { return 1; }
320