Unravel Engine C++ Reference
Loading...
Searching...
No Matches
thread.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "config.hpp"
4
5#include <thread>
6
7// An attempt at making a wrapper to deal with many Linuxes as well as Windows. Please edit as needed.
8#if UNRAVEL_PLATFORM_WINDOWS && (UNRAVEL_COMPILER_MSVC || UNRAVEL_COMPILER_CLANG)
9#define WIN32_LEAN_AND_MEAN
10#include <Windows.h>
11
12#ifdef min
13#undef min
14#endif
15
16#ifdef max
17#undef max
18#endif
19
20namespace platform
21{
22#pragma pack(push, 8)
23typedef struct tagTHREADNAME_INFO
24{
25 DWORD dwType; // Must be 0x1000.
26 LPCSTR szName; // Pointer to name (in user addr space).
27 DWORD dwThreadID; // Thread ID (-1=caller thread).
28 DWORD dwFlags; // Reserved for future use, must be zero.
29} THREADNAME_INFO;
30#pragma pack(pop)
31
32inline void set_thread_name(DWORD dwThreadID, const char* threadName)
33{
34 THREADNAME_INFO info;
35 info.dwType = 0x1000;
36 info.szName = threadName;
37 info.dwThreadID = dwThreadID;
38 info.dwFlags = 0;
39
40 static const DWORD MS_VC_EXCEPTION = 0x406D1388;
41
42 // Push an exception handler to ignore all following exceptions
43#pragma warning(push)
44#pragma warning(disable : 6320 6322)
45 __try
46 {
47 RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
48 }
49 __except(EXCEPTION_EXECUTE_HANDLER)
50 {
51 }
52#pragma warning(pop)
53}
54
55inline void set_thread_name(const char* threadName)
56{
57 DWORD threadId = ::GetCurrentThreadId();
58 //DWORD threadId = ::GetThreadId(reinterpret_cast<HANDLE>(thread.native_handle()));
59 set_thread_name(threadId, threadName);
60}
61
62inline auto get_boot_max_mhz() -> DWORD
63{
64 using get_max_proc_freq_fn = DWORD(WINAPI*)(DWORD, BYTE);
65 static const get_max_proc_freq_fn fn = []() -> get_max_proc_freq_fn
66 {
67 return reinterpret_cast<get_max_proc_freq_fn>(
68 GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "GetMaximumProcessorFrequency"));
69 }();
70 if(fn != nullptr)
71 {
72 const DWORD m = fn(0, 0);
73 if(m != 0)
74 {
75 return m;
76 }
77 }
78 return 3000;
79}
80
81inline auto get_thread_cpu_time_ns() -> int64_t
82{
83 // GetThreadTimes() is valid for "CPU time used" but its counters advance at the
84 // system timer resolution (~15.6 ms by default). Scoped measurements shorter than
85 // that almost always see identical user+kernel FILETIMEs -> delta 0 -> bogus 0% CPU.
86 //
87 // QueryThreadCycleTime() counts CPU cycles attributed to the thread while it runs;
88 // deltas are fine-grained. We convert cumulative cycles to approximate nanoseconds
89 // using the CPU's advertised max frequency (GetMaximumProcessorFrequency when present).
90 ULONG64 cycles = 0;
91 if(QueryThreadCycleTime(GetCurrentThread(), &cycles))
92 {
93 static const double ns_per_cycle = []() -> double
94 {
95 const DWORD mhz = get_boot_max_mhz();
96 const double hz = static_cast<double>(mhz) * 1e6;
97 return 1e9 / hz;
98 }();
99 return static_cast<int64_t>(static_cast<double>(cycles) * ns_per_cycle);
100 }
101
102 FILETIME creation, exit, kernel, user;
103 if(GetThreadTimes(GetCurrentThread(), &creation, &exit, &kernel, &user))
104 {
105 auto filetime_to_ns = [](const FILETIME& ft) -> int64_t
106 {
107 ULARGE_INTEGER li;
108 li.LowPart = ft.dwLowDateTime;
109 li.HighPart = ft.dwHighDateTime;
110 return static_cast<int64_t>(li.QuadPart) * 100;
111 };
112 return filetime_to_ns(kernel) + filetime_to_ns(user);
113 }
114 return 0;
115}
116} // namespace platform
117#elif UNRAVEL_PLATFORM_LINUX
118#include <pthread.h>
119#include <time.h>
120namespace platform
121{
122inline void set_thread_name(const char* threadName)
123{
124 pthread_setname_np(pthread_self(), threadName);
125}
126
127inline auto get_thread_cpu_time_ns() -> int64_t
128{
129 struct timespec ts{};
130 if(clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) == 0)
131 {
132 return static_cast<int64_t>(ts.tv_sec) * 1'000'000'000LL + static_cast<int64_t>(ts.tv_nsec);
133 }
134 return 0;
135}
136} // namespace platform
137#elif UNRAVEL_PLATFORM_OSX
138#include <pthread.h>
139#include <time.h>
140namespace platform
141{
142inline void set_thread_name(const char* threadName)
143{
144 pthread_setname_np(threadName);
145}
146
147inline auto get_thread_cpu_time_ns() -> int64_t
148{
149 struct timespec ts{};
150 if(clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) == 0)
151 {
152 return static_cast<int64_t>(ts.tv_sec) * 1'000'000'000LL + static_cast<int64_t>(ts.tv_nsec);
153 }
154 return 0;
155}
156} // namespace platform
157#else
158namespace platform
159{
160inline void set_thread_name(const char* threadName)
161{
162}
163
164inline auto get_thread_cpu_time_ns() -> int64_t
165{
166 return 0;
167}
168} // namespace platform
169#endif
170
void set_thread_name(const char *threadName)
Definition thread.hpp:160
auto get_thread_cpu_time_ns() -> int64_t
Definition thread.hpp:164