CppCommon 1.0.5.0
C++ Common Library
Loading...
Searching...
No Matches
thread.cpp
Go to the documentation of this file.
1
9#include "threads/thread.h"
10
11#include "system/cpu.h"
12#include "time/timestamp.h"
13
14#include <algorithm>
15
16#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
17#include <errno.h>
18#include <pthread.h>
19#include <sched.h>
20#include <time.h>
21#elif defined(_WIN32) || defined(_WIN64)
22#include <windows.h>
23#include <winternl.h>
24#undef Yield
25#undef max
26#undef min
27#define STATUS_SUCCESS 0x00000000
28#endif
29
30namespace CppCommon {
31
33namespace Internals {
34
35#if defined(_WIN32) || defined(_WIN64)
36// Helper function to set minimum resolution of the Windows Timer
37uint64_t SetMinimumTimerResolution()
38{
39 static NTSTATUS(__stdcall *NtQueryTimerResolution)(OUT PULONG MinimumResolution, OUT PULONG MaximumResolution, OUT PULONG ActualResolution) = (NTSTATUS(__stdcall*)(PULONG, PULONG, PULONG))GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQueryTimerResolution");
40 static NTSTATUS(__stdcall *NtSetTimerResolution)(IN ULONG RequestedResolution, IN BOOLEAN Set, OUT PULONG ActualResolution) = (NTSTATUS(__stdcall*)(ULONG, BOOLEAN, PULONG))GetProcAddress(GetModuleHandle("ntdll.dll"), "NtSetTimerResolution");
41
42 if ((NtQueryTimerResolution == nullptr) || (NtSetTimerResolution == nullptr))
43 return 0;
44
45 ULONG MinimumResolution, MaximumResolution, ActualResolution;
46 NTSTATUS ns = NtQueryTimerResolution(&MinimumResolution, &MaximumResolution, &ActualResolution);
47 if (ns == STATUS_SUCCESS)
48 {
49 ns = NtSetTimerResolution(std::min(MinimumResolution, MaximumResolution), TRUE, &ActualResolution);
50 if (ns == STATUS_SUCCESS)
51 return (ActualResolution * 100);
52 }
53 return 1000000;
54}
55#endif
56
57} // namespace Internals
59
60uint64_t Thread::CurrentThreadId() noexcept
61{
62#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
63 return (uint64_t)pthread_self();
64#elif defined(_WIN32) || defined(_WIN64)
65 return GetCurrentThreadId();
66#else
67 #error Unsupported platform
68#endif
69}
70
72{
73#if defined(__APPLE__) || defined(__CYGWIN__)
74 return 0;
75#elif defined(unix) || defined(__unix) || defined(__unix__)
76 int affinity = sched_getcpu();
77 return (affinity < 0) ? 0 : affinity;
78#elif defined(_WIN32) || defined(_WIN64)
79 return GetCurrentProcessorNumber();
80#endif
81}
82
83void Thread::SleepFor(const Timespan& timespan) noexcept
84{
85 if (timespan < 0)
86 return;
87 if (timespan == 0)
88 return Yield();
89#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
90 struct timespec req, rem;
91 req.tv_sec = timespan.seconds();
92 req.tv_nsec = timespan.nanoseconds() % 1000000000;
93
94 // Call nanosleep() in loop until we have remaining time to sleep
95 while (nanosleep(&req, &rem) != 0)
96 {
97 if (errno == EINTR)
98 req = rem;
99 else
100 break;
101 }
102#elif defined(_WIN32) || defined(_WIN64)
103 static NTSTATUS(__stdcall *NtDelayExecution)(IN BOOLEAN Alertable, IN PLARGE_INTEGER DelayInterval) = (NTSTATUS(__stdcall*)(BOOLEAN, PLARGE_INTEGER))GetProcAddress(GetModuleHandle("ntdll.dll"), "NtDelayExecution");
104
105 // Update once and get Windows Timer resolution
106 static int64_t resolution = Internals::SetMinimumTimerResolution();
107
108 int64_t sleep = timespan.nanoseconds();
109 int64_t yield = timespan.nanoseconds() % resolution;
110
111 // Yield to other thread for a short time
112 if (yield > 0)
113 {
114 int64_t current = Timestamp::nano();
115 do
116 {
117 SwitchToThread();
118 int64_t temp = Timestamp::nano() - current;
119 sleep -= temp;
120 yield -= temp;
121 } while (yield > 0);
122 }
123
124 // Sleep if we have enough time
125 if (sleep > 0)
126 {
127 if (NtDelayExecution != nullptr)
128 {
129 // Sleep with microsecond precision
130 LARGE_INTEGER interval;
131 interval.QuadPart = -sleep / 100;
132 NtDelayExecution(FALSE, &interval);
133 }
134 else
135 {
136 // Sleep with millisecond precision
137 Sleep(sleep / 1000000);
138 }
139 }
140#endif
141}
142
143void Thread::Yield() noexcept
144{
145#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
146 sched_yield();
147#elif defined(_WIN32) || defined(_WIN64)
148 SwitchToThread();
149#endif
150}
151
152std::bitset<64> Thread::GetAffinity()
153{
154#if defined(__APPLE__) || defined(__CYGWIN__)
155 return std::bitset<64>(0xFFFFFFFFFFFFFFFFull >> (64 - CPU::Affinity()));
156#elif defined(unix) || defined(__unix) || defined(__unix__)
157 cpu_set_t cpuset;
158 CPU_ZERO(&cpuset);
159 int result = pthread_getaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
160 if (result != 0)
161 throwex SystemException("Failed to get the current thread CPU affinity!");
162 std::bitset<64> affinity;
163 for (int i = 0; i < std::min(CPU_SETSIZE, 64); ++i)
164 if (CPU_ISSET(i, &cpuset))
165 affinity.set(i);
166 return affinity;
167#elif defined(_WIN32) || defined(_WIN64)
168 typedef LONG KPRIORITY;
169
170 typedef struct _NT_CLIENT_ID
171 {
172 HANDLE UniqueProcess;
173 HANDLE UniqueThread;
174 } NT_CLIENT_ID;
175
176 typedef struct _THREAD_BASIC_INFORMATION
177 {
178 NTSTATUS ExitStatus;
179 PTEB TebBaseAddress;
180 NT_CLIENT_ID ClientId;
181 ULONG_PTR AffinityMask;
182 KPRIORITY Priority;
183 LONG BasePriority;
184 } THREAD_BASIC_INFORMATION;
185
186 static NTSTATUS(__stdcall *NtQueryInformationThread)(IN HANDLE ThreadHandle, IN THREADINFOCLASS ThreadInformationClass, OUT PVOID ThreadInformation, IN ULONG ThreadInformationLength, OUT PULONG ReturnLength) = (NTSTATUS(__stdcall*)(HANDLE, THREADINFOCLASS, PVOID, ULONG, PULONG))GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQueryInformationThread");
187
188 if (NtQueryInformationThread != nullptr)
189 {
190 THREAD_BASIC_INFORMATION tbi;
191 ZeroMemory(&tbi, sizeof(tbi));
192 NTSTATUS ns = NtQueryInformationThread(GetCurrentThread(), (THREADINFOCLASS)0, &tbi, sizeof(tbi), nullptr);
193 if (ns == STATUS_SUCCESS)
194 return std::bitset<64>(tbi.AffinityMask);
195 else
196 throwex SystemException("Failed to get the current thread CPU affinity!");
197 }
198
199 return std::bitset<64>(0xFFFFFFFFFFFFFFFFull >> (64 - CPU::Affinity()));
200#endif
201}
202
203std::bitset<64> Thread::GetAffinity(std::thread& thread)
204{
205#if defined(__APPLE__) || defined(__CYGWIN__)
206 return std::bitset<64>(0xFFFFFFFFFFFFFFFFull >> (64 - CPU::Affinity()));
207#elif defined(unix) || defined(__unix) || defined(__unix__)
208 cpu_set_t cpuset;
209 CPU_ZERO(&cpuset);
210 int result = pthread_getaffinity_np(thread.native_handle(), sizeof(cpu_set_t), &cpuset);
211 if (result != 0)
212 throwex SystemException("Failed to get the given thread CPU affinity!");
213 std::bitset<64> affinity;
214 for (int i = 0; i < std::min(CPU_SETSIZE, 64); ++i)
215 if (CPU_ISSET(i, &cpuset))
216 affinity.set(i);
217 return affinity;
218#elif defined(_WIN32) || defined(_WIN64)
219 typedef LONG KPRIORITY;
220
221 typedef struct _NT_CLIENT_ID
222 {
223 HANDLE UniqueProcess;
224 HANDLE UniqueThread;
225 } NT_CLIENT_ID;
226
227 typedef struct _THREAD_BASIC_INFORMATION
228 {
229 NTSTATUS ExitStatus;
230 PTEB TebBaseAddress;
231 NT_CLIENT_ID ClientId;
232 ULONG_PTR AffinityMask;
233 KPRIORITY Priority;
234 LONG BasePriority;
235 } THREAD_BASIC_INFORMATION;
236
237 static NTSTATUS(__stdcall *NtQueryInformationThread)(IN HANDLE ThreadHandle, IN THREADINFOCLASS ThreadInformationClass, OUT PVOID ThreadInformation, IN ULONG ThreadInformationLength, OUT PULONG ReturnLength) = (NTSTATUS(__stdcall*)(HANDLE, THREADINFOCLASS, PVOID, ULONG, PULONG))GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQueryInformationThread");
238
239 if (NtQueryInformationThread != nullptr)
240 {
241 THREAD_BASIC_INFORMATION tbi;
242 ZeroMemory(&tbi, sizeof(tbi));
243 NTSTATUS ns = NtQueryInformationThread((HANDLE)thread.native_handle(), (THREADINFOCLASS)0, &tbi, sizeof(tbi), nullptr);
244 if (ns == STATUS_SUCCESS)
245 return std::bitset<64>(tbi.AffinityMask);
246 else
247 throwex SystemException("Failed to get the given thread CPU affinity!");
248 }
249
250 return std::bitset<64>(0xFFFFFFFFFFFFFFFFull >> (64 - CPU::Affinity()));
251#endif
252}
253
254void Thread::SetAffinity(const std::bitset<64>& affinity)
255{
256#if defined(__APPLE__)
257 throwex SystemException("Apple platform does not allow to set the current thread CPU affinity!");
258#elif defined(__CYGWIN__)
259 throwex SystemException("Cygwin platform does not allow to set the current thread CPU affinity!");
260#elif defined(unix) || defined(__unix) || defined(__unix__)
261 cpu_set_t cpuset;
262 CPU_ZERO(&cpuset);
263 for (int i = 0; i < std::min(CPU_SETSIZE, 64); ++i)
264 if (affinity[i])
265 CPU_SET(i, &cpuset);
266 int result = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
267 if (result != 0)
268 throwex SystemException("Failed to set the current thread CPU affinity!");
269#elif defined(_WIN32) || defined(_WIN64)
270 DWORD_PTR dwThreadAffinityMask = (DWORD_PTR)affinity.to_ullong();
271 if (!SetThreadAffinityMask(GetCurrentThread(), dwThreadAffinityMask))
272 throwex SystemException("Failed to set the current thread CPU affinity!");
273#endif
274}
275
276void Thread::SetAffinity(std::thread& thread, const std::bitset<64>& affinity)
277{
278#if defined(__APPLE__)
279 throwex SystemException("Apple platform does not allow to set the given thread CPU affinity!");
280#elif defined(__CYGWIN__)
281 throwex SystemException("Cygwin platform does not allow to set the given thread CPU affinity!");
282#elif defined(unix) || defined(__unix) || defined(__unix__)
283 cpu_set_t cpuset;
284 CPU_ZERO(&cpuset);
285 for (int i = 0; i < std::min(CPU_SETSIZE, 64); ++i)
286 if (affinity[i])
287 CPU_SET(i, &cpuset);
288 int result = pthread_setaffinity_np(thread.native_handle(), sizeof(cpu_set_t), &cpuset);
289 if (result != 0)
290 throwex SystemException("Failed to set the given thread CPU affinity!");
291#elif defined(_WIN32) || defined(_WIN64)
292 DWORD_PTR dwThreadAffinityMask = (DWORD_PTR)affinity.to_ullong();
293 if (!SetThreadAffinityMask((HANDLE)thread.native_handle(), dwThreadAffinityMask))
294 throwex SystemException("Failed to set the given thread CPU affinity!");
295#endif
296}
297
299{
300#if defined(__CYGWIN__)
302#elif defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
303 int policy;
304 struct sched_param sched;
305 int result = pthread_getschedparam(pthread_self(), &policy, &sched);
306 if (result != 0)
307 throwex SystemException("Failed to get the current thread priority!");
308 if ((policy == SCHED_FIFO) || (policy == SCHED_RR))
309 {
310 if (sched.sched_priority < 15)
312 else if (sched.sched_priority < 30)
314 else if (sched.sched_priority < 50)
315 return ThreadPriority::LOW;
316 else if (sched.sched_priority < 70)
318 else if (sched.sched_priority < 85)
320 else if (sched.sched_priority < 99)
322 else
324 }
325 else
327#elif defined(_WIN32) || defined(_WIN64)
328 int priority = GetThreadPriority(GetCurrentThread());
329 if (priority == THREAD_PRIORITY_ERROR_RETURN)
330 throwex SystemException("Failed to get the current thread priority!");
331 if (priority < THREAD_PRIORITY_LOWEST)
333 else if (priority < THREAD_PRIORITY_BELOW_NORMAL)
335 else if (priority < THREAD_PRIORITY_NORMAL)
336 return ThreadPriority::LOW;
337 else if (priority < THREAD_PRIORITY_ABOVE_NORMAL)
339 else if (priority < THREAD_PRIORITY_HIGHEST)
341 else if (priority < THREAD_PRIORITY_TIME_CRITICAL)
343 else
345#endif
346}
347
349{
350#if defined(__CYGWIN__)
352#elif defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
353 int policy;
354 struct sched_param sched;
355 int result = pthread_getschedparam(thread.native_handle(), &policy, &sched);
356 if (result != 0)
357 throwex SystemException("Failed to get the given thread priority!");
358 if ((policy == SCHED_FIFO) || (policy == SCHED_RR))
359 {
360 if (sched.sched_priority < 15)
362 else if (sched.sched_priority < 30)
364 else if (sched.sched_priority < 50)
365 return ThreadPriority::LOW;
366 else if (sched.sched_priority < 70)
368 else if (sched.sched_priority < 85)
370 else if (sched.sched_priority < 99)
372 else
374 }
375 else
377#elif defined(_WIN32) || defined(_WIN64)
378 int priority = GetThreadPriority((HANDLE)thread.native_handle());
379 if (priority == THREAD_PRIORITY_ERROR_RETURN)
380 throwex SystemException("Failed to get the given thread priority!");
381 if (priority < THREAD_PRIORITY_LOWEST)
383 else if (priority < THREAD_PRIORITY_BELOW_NORMAL)
385 else if (priority < THREAD_PRIORITY_NORMAL)
386 return ThreadPriority::LOW;
387 else if (priority < THREAD_PRIORITY_ABOVE_NORMAL)
389 else if (priority < THREAD_PRIORITY_HIGHEST)
391 else if (priority < THREAD_PRIORITY_TIME_CRITICAL)
393 else
395#endif
396}
397
399{
400#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
401 int policy = SCHED_RR;
402 struct sched_param sched;
403 sched.sched_priority = 50;
404 switch (priority)
405 {
407 sched.sched_priority = 1;
408 break;
410 sched.sched_priority = 15;
411 break;
413 sched.sched_priority = 30;
414 break;
416 sched.sched_priority = 50;
417 break;
419 sched.sched_priority = 70;
420 break;
422 sched.sched_priority = 85;
423 break;
425 sched.sched_priority = 99;
426 break;
427 }
428
429 int result = pthread_setschedparam(pthread_self(), policy, &sched);
430 if (result != 0)
431 throwex SystemException("Failed to set the current thread priority!");
432#elif defined(_WIN32) || defined(_WIN64)
433 int nPriority = THREAD_PRIORITY_NORMAL;
434 switch (priority)
435 {
437 nPriority = THREAD_PRIORITY_IDLE;
438 break;
440 nPriority = THREAD_PRIORITY_LOWEST;
441 break;
443 nPriority = THREAD_PRIORITY_BELOW_NORMAL;
444 break;
446 nPriority = THREAD_PRIORITY_NORMAL;
447 break;
449 nPriority = THREAD_PRIORITY_ABOVE_NORMAL;
450 break;
452 nPriority = THREAD_PRIORITY_HIGHEST;
453 break;
455 nPriority = THREAD_PRIORITY_TIME_CRITICAL;
456 break;
457 }
458
459 if (!SetThreadPriority(GetCurrentThread(), nPriority))
460 throwex SystemException("Failed to set the current thread priority!");
461#endif
462}
463
464void Thread::SetPriority(std::thread& thread, ThreadPriority priority)
465{
466#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
467 int policy = SCHED_RR;
468 struct sched_param sched;
469 sched.sched_priority = 50;
470 switch (priority)
471 {
473 sched.sched_priority = 1;
474 break;
476 sched.sched_priority = 15;
477 break;
479 sched.sched_priority = 30;
480 break;
482 sched.sched_priority = 50;
483 break;
485 sched.sched_priority = 70;
486 break;
488 sched.sched_priority = 85;
489 break;
491 sched.sched_priority = 99;
492 break;
493 }
494
495 int result = pthread_setschedparam(thread.native_handle(), policy, &sched);
496 if (result != 0)
497 throwex SystemException("Failed to set the given thread priority!");
498#elif defined(_WIN32) || defined(_WIN64)
499 int nPriority = THREAD_PRIORITY_NORMAL;
500 switch (priority)
501 {
503 nPriority = THREAD_PRIORITY_IDLE;
504 break;
506 nPriority = THREAD_PRIORITY_LOWEST;
507 break;
509 nPriority = THREAD_PRIORITY_BELOW_NORMAL;
510 break;
512 nPriority = THREAD_PRIORITY_NORMAL;
513 break;
515 nPriority = THREAD_PRIORITY_ABOVE_NORMAL;
516 break;
518 nPriority = THREAD_PRIORITY_HIGHEST;
519 break;
521 nPriority = THREAD_PRIORITY_TIME_CRITICAL;
522 break;
523 }
524
525 if (!SetThreadPriority((HANDLE)thread.native_handle(), nPriority))
526 throwex SystemException("Failed to set the given thread priority!");
527#endif
528}
529
530} // namespace CppCommon
static int Affinity()
CPU affinity count.
Definition cpu.cpp:95
System exception.
Definition exceptions.h:107
static ThreadPriority GetPriority()
Get the current thread priority.
Definition thread.cpp:298
static uint64_t CurrentThreadId() noexcept
Get the current thread Id.
Definition thread.cpp:60
static void SleepFor(const Timespan &timespan) noexcept
Sleep the current thread for the given timespan.
Definition thread.cpp:83
static void SetPriority(ThreadPriority priority)
Set the current thread priority.
Definition thread.cpp:398
static std::bitset< 64 > GetAffinity()
Get the current thread CPU affinity bitset.
Definition thread.cpp:152
static void Yield() noexcept
Yield to other threads.
Definition thread.cpp:143
static uint32_t CurrentThreadAffinity() noexcept
Get the current thread CPU affinity.
Definition thread.cpp:71
static void SetAffinity(const std::bitset< 64 > &affinity)
Set the current thread CPU affinity bitset.
Definition thread.cpp:254
static uint64_t nano()
Get the high resolution timestamp.
CPU management definition.
#define throwex
Throw extended exception macro.
Definition exceptions.h:23
C++ Common project definitions.
ThreadPriority
Thread priorities.
Definition thread.h:28
@ NORMAL
Normal thread priority.
@ LOW
Low thread priority.
@ REALTIME
Realtime thread priority.
@ LOWEST
Lowest thread priority.
@ IDLE
Idle thread priority.
@ HIGH
High thread priority.
@ HIGHEST
Highest thread priority.
Thread definition.
Time definition.
Timestamp definition.