CppCommon  1.0.4.1
C++ Common Library
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 
30 namespace CppCommon {
31 
33 namespace Internals {
34 
35 #if defined(_WIN32) || defined(_WIN64)
36 // Helper function to set minimum resolution of the Windows Timer
37 uint64_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 
60 uint64_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 
71 uint32_t Thread::CurrentThreadAffinity() noexcept
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 
83 void 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 
143 void 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 
152 std::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 
203 std::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 
254 void 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 
276 void 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__)
301  return ThreadPriority::NORMAL;
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)
311  return ThreadPriority::IDLE;
312  else if (sched.sched_priority < 30)
313  return ThreadPriority::LOWEST;
314  else if (sched.sched_priority < 50)
315  return ThreadPriority::LOW;
316  else if (sched.sched_priority < 70)
317  return ThreadPriority::NORMAL;
318  else if (sched.sched_priority < 85)
319  return ThreadPriority::HIGH;
320  else if (sched.sched_priority < 99)
322  else
324  }
325  else
326  return ThreadPriority::NORMAL;
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)
332  return ThreadPriority::IDLE;
333  else if (priority < THREAD_PRIORITY_BELOW_NORMAL)
334  return ThreadPriority::LOWEST;
335  else if (priority < THREAD_PRIORITY_NORMAL)
336  return ThreadPriority::LOW;
337  else if (priority < THREAD_PRIORITY_ABOVE_NORMAL)
338  return ThreadPriority::NORMAL;
339  else if (priority < THREAD_PRIORITY_HIGHEST)
340  return ThreadPriority::HIGH;
341  else if (priority < THREAD_PRIORITY_TIME_CRITICAL)
343  else
345 #endif
346 }
347 
349 {
350 #if defined(__CYGWIN__)
351  return ThreadPriority::NORMAL;
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)
361  return ThreadPriority::IDLE;
362  else if (sched.sched_priority < 30)
363  return ThreadPriority::LOWEST;
364  else if (sched.sched_priority < 50)
365  return ThreadPriority::LOW;
366  else if (sched.sched_priority < 70)
367  return ThreadPriority::NORMAL;
368  else if (sched.sched_priority < 85)
369  return ThreadPriority::HIGH;
370  else if (sched.sched_priority < 99)
372  else
374  }
375  else
376  return ThreadPriority::NORMAL;
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)
382  return ThreadPriority::IDLE;
383  else if (priority < THREAD_PRIORITY_BELOW_NORMAL)
384  return ThreadPriority::LOWEST;
385  else if (priority < THREAD_PRIORITY_NORMAL)
386  return ThreadPriority::LOW;
387  else if (priority < THREAD_PRIORITY_ABOVE_NORMAL)
388  return ThreadPriority::NORMAL;
389  else if (priority < THREAD_PRIORITY_HIGHEST)
390  return ThreadPriority::HIGH;
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;
412  case ThreadPriority::LOW:
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;
442  case ThreadPriority::LOW:
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 
464 void 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;
478  case ThreadPriority::LOW:
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;
508  case ThreadPriority::LOW:
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:93
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.
Definition: timestamp.cpp:153
CPU management definition.
#define throwex
Throw extended exception macro.
Definition: exceptions.h:23
C++ Common project definitions.
Definition: token_bucket.h:15
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.