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