quill
ThreadUtilities.h
1 
7 #pragma once
8 
9 #include "quill/core/Attributes.h"
10 #include "quill/core/QuillError.h"
11 
12 #include <cstdint>
13 #include <cstring>
14 #include <string>
15 
16 #if defined(_WIN32)
17  #if !defined(WIN32_LEAN_AND_MEAN)
18  #define WIN32_LEAN_AND_MEAN
19  #endif
20 
21  #if !defined(NOMINMAX)
22  // Mingw already defines this, so no need to redefine
23  #define NOMINMAX
24  #endif
25 
26  #include <windows.h>
27 
28  #include <codecvt>
29  #include <locale>
30 #elif defined(__APPLE__)
31  #include <mach/thread_act.h>
32  #include <mach/thread_policy.h>
33  #include <pthread.h>
34 #elif defined(__NetBSD__)
35  #include <lwp.h>
36  #include <pthread.h>
37  #include <unistd.h>
38 #elif defined(__FreeBSD__)
39  #include <pthread_np.h>
40  #include <sys/thr.h>
41  #include <unistd.h>
42 #elif defined(__DragonFly__)
43  #include <pthread_np.h>
44  #include <sys/lwp.h>
45  #include <unistd.h>
46 #elif defined(__OpenBSD__)
47  #include <pthread_np.h>
48  #include <unistd.h>
49 #else
50  // linux
51  #include <pthread.h>
52  #include <sys/syscall.h>
53  #include <unistd.h>
54 #endif
55 
56 QUILL_BEGIN_NAMESPACE
57 
58 namespace detail
59 {
60 #if defined(_WIN32) && defined(_MSC_VER)
61 
67 QUILL_NODISCARD inline std::wstring s2ws(std::string const& str) noexcept
68 {
69  #pragma warning(push)
70  #pragma warning(disable : 4996)
71 
72  using convert_t = std::codecvt_utf8_utf16<wchar_t>;
73  std::wstring_convert<convert_t, wchar_t> converter;
74  return converter.from_bytes(str);
75 
76  #pragma warning(pop)
77 }
78 
84 QUILL_NODISCARD inline std::string ws2s(std::wstring const& wstr) noexcept
85 {
86  #pragma warning(push)
87  #pragma warning(disable : 4996)
88 
89  using convert_t = std::codecvt_utf8_utf16<wchar_t>;
90  std::wstring_convert<convert_t, wchar_t> converter;
91  return converter.to_bytes(wstr);
92 
93  #pragma warning(pop)
94 }
95 
96 /***/
97 template <typename ReturnT, typename Signature, typename... Args>
98 ReturnT callRunTimeDynamicLinkedFunction(std::string const& dll_name,
99  std::string const& function_name, Args... args)
100 {
101  // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getthreaddescription
102  // Windows Server 2016, Windows 10 LTSB 2016 and Windows 10 version 1607:
103  // GetThreadDescription is only available by Run Time Dynamic Linking in KernelBase.dll.
104 
105  #ifdef UNICODE
106  const HINSTANCE hinstLibrary = LoadLibraryW(s2ws(dll_name).c_str());
107  #else
108  const HINSTANCE hinstLibrary = LoadLibraryA(dll_name.c_str());
109  #endif
110 
111  if (QUILL_UNLIKELY(hinstLibrary == nullptr))
112  {
113  QUILL_THROW(QuillError{std::string{"Failed to load library " + dll_name}});
114  }
115 
116  // Using a C-style cast or memcpy to avoid Clang's strict function pointer casting rules
117  FARPROC proc_address = GetProcAddress(hinstLibrary, function_name.c_str());
118 
119  if (QUILL_UNLIKELY(proc_address == nullptr))
120  {
121  FreeLibrary(hinstLibrary);
122  QUILL_THROW(QuillError{std::string{"Failed to call " + function_name + " " + dll_name}});
123  }
124 
125  // Use memcpy to avoid the strict cast warning in Clang
126  Signature callable;
127  memcpy(&callable, &proc_address, sizeof(proc_address));
128 
129  ReturnT const hr = callable(static_cast<Args&&>(args)...);
130  BOOL const fFreeResult = FreeLibrary(hinstLibrary);
131 
132  if (QUILL_UNLIKELY(!fFreeResult))
133  {
134  QUILL_THROW(QuillError{std::string{"Failed to free library " + dll_name}});
135  }
136 
137  return hr;
138 }
139 #endif
140 
147 QUILL_NODISCARD QUILL_EXPORT QUILL_ATTRIBUTE_USED inline std::string get_thread_name()
148 {
149 #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(__ANDROID__) || \
150  defined(QUILL_NO_THREAD_NAME_SUPPORT)
151  // Disabled on MINGW / Cygwin.
152  return std::string{"ThreadNameDisabled"};
153 #elif defined(_WIN32)
154  PWSTR data = nullptr;
155 
156  typedef HRESULT(WINAPI * GetThreadDescriptionSignature)(HANDLE, PWSTR*);
157  HRESULT hr = callRunTimeDynamicLinkedFunction<HRESULT, GetThreadDescriptionSignature>(
158  "KernelBase.dll", "GetThreadDescription", GetCurrentThread(), &data);
159 
160  if (FAILED(hr))
161  {
162  QUILL_THROW(QuillError{"Failed to get thread name"});
163  }
164 
165  if (QUILL_UNLIKELY(data == nullptr))
166  {
167  QUILL_THROW(QuillError{"Failed to get thread name. Invalid data."});
168  }
169 
170  std::wstring const wide_name{data, wcsnlen_s(data, 256)};
171  LocalFree(data);
172  return ws2s(wide_name);
173 #else
174  // Apple, linux
175  char thread_name[16] = {'\0'};
176  #if defined(__OpenBSD__) || defined(__FreeBSD__)
177  pthread_get_name_np(pthread_self(), &thread_name[0], 16);
178  #else
179  auto res = pthread_getname_np(pthread_self(), &thread_name[0], 16);
180  if (res != 0)
181  {
182  QUILL_THROW(QuillError{"Failed to get thread name. error: " + std::to_string(res)});
183  }
184  #endif
185  return std::string{&thread_name[0], strlen(&thread_name[0])};
186 #endif
187 }
188 
193 QUILL_NODISCARD QUILL_EXPORT QUILL_ATTRIBUTE_USED inline uint32_t get_thread_id() noexcept
194 {
195 #if defined(__CYGWIN__)
196  // get thread id on cygwin not supported
197  return 0;
198 #elif defined(_WIN32)
199  return static_cast<uint32_t>(GetCurrentThreadId());
200 #elif defined(__linux__)
201  return static_cast<uint32_t>(::syscall(SYS_gettid));
202 #elif defined(__APPLE__)
203  uint64_t tid64;
204  pthread_threadid_np(nullptr, &tid64);
205  return static_cast<uint32_t>(tid64);
206 #elif defined(__NetBSD__)
207  return static_cast<uint32_t>(_lwp_self());
208 #elif defined(__FreeBSD__)
209  long lwpid;
210  thr_self(&lwpid);
211  return static_cast<uint32_t>(lwpid);
212 #elif defined(__DragonFly__)
213  return static_cast<uint32_t>(lwp_gettid());
214 #elif defined(__OpenBSD__)
215  return static_cast<uint32_t>(getthrid());
216 #else
217  return reinterpret_cast<uintptr_t>(pthread_self()); // (Ab)use pthread_self as a last resort option
218 #endif
219 }
220 
221 } // namespace detail
222 
223 QUILL_END_NAMESPACE
QUILL_NODISCARD QUILL_EXPORT QUILL_ATTRIBUTE_USED std::string get_thread_name()
Returns the name of the thread.
Definition: ThreadUtilities.h:147
Setups a signal handler to handle fatal signals.
Definition: BackendManager.h:24
custom exception
Definition: QuillError.h:45
QUILL_NODISCARD QUILL_EXPORT QUILL_ATTRIBUTE_USED uint32_t get_thread_id() noexcept
Returns the os assigned ID of the thread.
Definition: ThreadUtilities.h:193