piranha  0.10
thread_management.hpp
1 /* Copyright 2009-2017 Francesco Biscani (bluescarni@gmail.com)
2 
3 This file is part of the Piranha library.
4 
5 The Piranha library is free software; you can redistribute it and/or modify
6 it under the terms of either:
7 
8  * the GNU Lesser General Public License as published by the Free
9  Software Foundation; either version 3 of the License, or (at your
10  option) any later version.
11 
12 or
13 
14  * the GNU General Public License as published by the Free Software
15  Foundation; either version 3 of the License, or (at your option) any
16  later version.
17 
18 or both in parallel, as here.
19 
20 The Piranha library is distributed in the hope that it will be useful, but
21 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
22 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23 for more details.
24 
25 You should have received copies of the GNU General Public License and the
26 GNU Lesser General Public License along with the Piranha library. If not,
27 see https://www.gnu.org/licenses/. */
28 
29 #ifndef PIRANHA_THREAD_MANAGEMENT_HPP
30 #define PIRANHA_THREAD_MANAGEMENT_HPP
31 
32 #include <stdexcept>
33 #include <utility>
34 
35 #include <piranha/config.hpp>
36 #include <piranha/exceptions.hpp>
37 #include <piranha/runtime_info.hpp>
38 #include <piranha/safe_cast.hpp>
39 #include <piranha/thread_management.hpp>
40 
41 #if defined(PIRANHA_HAVE_PTHREAD_AFFINITY)
42 
43 extern "C" {
44 #include <pthread.h>
45 #include <sched.h>
46 }
47 
48 namespace piranha
49 {
50 
51 inline namespace impl
52 {
53 
54 // In old systems (CentOS 5) CPU_COUNT is not defined. We define a small wrapper
55 // that implements the functionality if it is missing. See:
56 // http://www.redhat.com/archives/libvir-list/2012-August/msg01515.html
57 inline int posix_cpu_count(::cpu_set_t *set)
58 {
59 #if defined(CPU_COUNT)
60  return CPU_COUNT(set);
61 #else
62  int count = 0;
63  for (int i = 0; i < CPU_SETSIZE; ++i) {
64  if (CPU_ISSET(i, set)) {
65  ++count;
66  }
67  }
68  return count;
69 #endif
70 }
71 }
72 }
73 
74 #elif defined(__FreeBSD__)
75 
76 extern "C" {
77 #include <sys/cpuset.h>
78 #include <sys/param.h>
79 }
80 
81 #elif defined(_WIN32)
82 
83 extern "C" {
84 #include <Windows.h>
85 }
86 #include <limits>
87 
88 #endif
89 
90 namespace piranha
91 {
92 
94 
109 inline void bind_to_proc(unsigned n)
110 {
111  const auto hc = runtime_info::get_hardware_concurrency();
112  if (hc != 0u && n >= hc) {
113  piranha_throw(std::invalid_argument, "processor index is larger than the detected hardware concurrency");
114  }
115 #if defined(PIRANHA_HAVE_PTHREAD_AFFINITY)
116  unsigned cpu_setsize;
117  int n_int;
118  try {
119  cpu_setsize = safe_cast<unsigned>(CPU_SETSIZE);
120  n_int = safe_cast<int>(n);
121  } catch (const safe_cast_failure &) {
122  piranha_throw(std::runtime_error, "numeric conversion error");
123  }
124  if (n >= cpu_setsize) {
125  piranha_throw(std::invalid_argument, "processor index is larger than the maximum allowed value");
126  }
127  cpu_set_t cpuset;
128  CPU_ZERO(&cpuset);
129  CPU_SET(n_int, &cpuset);
130  const int errno_ = ::pthread_setaffinity_np(::pthread_self(), sizeof(cpuset), &cpuset);
131  if (errno_ != 0) {
132  piranha_throw(std::runtime_error, "the call to pthread_setaffinity_np() failed");
133  }
134 #elif defined(__FreeBSD__)
135  unsigned cpu_setsize;
136  int n_int;
137  try {
138  cpu_setsize = safe_cast<unsigned>(CPU_SETSIZE);
139  n_int = safe_cast<int>(n);
140  } catch (const safe_cast_failure &) {
141  piranha_throw(std::runtime_error, "numeric conversion error");
142  }
143  if (n >= cpu_setsize) {
144  piranha_throw(std::invalid_argument, "processor index is larger than the maximum allowed value");
145  }
146  cpuset_t cpuset;
147  CPU_ZERO(&cpuset);
148  CPU_SET(n, &cpuset);
149  if (::cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, -1, sizeof(cpuset), &cpuset) == -1) {
150  piranha_throw(std::runtime_error, "the call to cpuset_setaffinity() failed");
151  }
152 #elif defined(_WIN32)
153  // Check we are not going to bit shift too much.
154  if (n >= static_cast<unsigned>(std::numeric_limits<DWORD_PTR>::digits)) {
155  piranha_throw(std::invalid_argument, "processor index is larger than the maximum allowed value");
156  }
157  if (::SetThreadAffinityMask(::GetCurrentThread(), (DWORD_PTR)1 << n) == 0) {
158  piranha_throw(std::runtime_error, "the call to SetThreadAffinityMask() failed");
159  }
160 #else
161  piranha_throw(not_implemented_error, "bind_to_proc() is not available on this platform");
162 #endif
163 }
164 
166 
180 inline std::pair<bool, unsigned> bound_proc()
181 {
182 #if defined(PIRANHA_HAVE_PTHREAD_AFFINITY)
183  cpu_set_t cpuset;
184  CPU_ZERO(&cpuset);
185  const int errno_ = ::pthread_getaffinity_np(::pthread_self(), sizeof(cpuset), &cpuset);
186  if (errno_ != 0) {
187  piranha_throw(std::runtime_error, "the call to pthread_getaffinity_np() failed");
188  }
189  const int cpu_count = posix_cpu_count(&cpuset);
190  if (cpu_count == 0 || cpu_count > 1) {
191  return std::make_pair(false, static_cast<unsigned>(0));
192  }
193  int cpu_setsize;
194  try {
195  cpu_setsize = safe_cast<int>(CPU_SETSIZE);
196  } catch (const safe_cast_failure &) {
197  piranha_throw(std::runtime_error, "numeric conversion error");
198  }
199  for (int i = 0; i < cpu_setsize; ++i) {
200  if (CPU_ISSET(i, &cpuset)) {
201  // Cast is safe here (verified above that cpu_setsize is representable in int,
202  // and, by extension, in unsigned).
203  return std::make_pair(true, static_cast<unsigned>(i));
204  }
205  }
206  piranha_throw(std::runtime_error, "operation failed");
207 #elif defined(__FreeBSD__)
208  cpuset_t cpuset;
209  CPU_ZERO(&cpuset);
210  const int errno_ = ::cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, -1, sizeof(cpuset), &cpuset);
211  if (errno_ != 0) {
212  piranha_throw(std::runtime_error, "the call to ::cpuset_getaffinity() failed");
213  }
214  int cpu_setsize;
215  try {
216  cpu_setsize = safe_cast<int>(CPU_SETSIZE);
217  } catch (const safe_cast_failure &) {
218  piranha_throw(std::runtime_error, "numeric conversion error");
219  }
220  unsigned bound_cpus = 0;
221  int candidate = 0;
222  for (int i = 0; i < cpu_setsize; ++i) {
223  if (CPU_ISSET(i, &cpuset)) {
224  ++bound_cpus;
225  candidate = i;
226  }
227  }
228  if (bound_cpus == 0 || bound_cpus > 1) {
229  return std::make_pair(false, static_cast<unsigned>(0));
230  } else {
231  return std::make_pair(true, static_cast<unsigned>(candidate));
232  }
233 #elif defined(_WIN32)
234  // Store the original affinity mask.
235  const DWORD_PTR original_affinity_mask = ::SetThreadAffinityMask(::GetCurrentThread(), (DWORD_PTR)1);
236  if (original_affinity_mask == 0) {
237  piranha_throw(std::runtime_error, "the call to SetThreadAffinityMask() failed");
238  }
239  const unsigned cpu_setsize = static_cast<unsigned>(std::numeric_limits<DWORD_PTR>::digits);
240  unsigned bound_cpus = 0, candidate = 0;
241  for (unsigned i = 0; i < cpu_setsize; ++i) {
242  if (((DWORD_PTR)1 << i) & original_affinity_mask) {
243  ++bound_cpus;
244  candidate = i;
245  }
246  }
247  // Restore the original affinity mask.
248  if (::SetThreadAffinityMask(::GetCurrentThread(), original_affinity_mask) == 0) {
249  piranha_throw(std::runtime_error, "the call to SetThreadAffinityMask() failed");
250  }
251  if (bound_cpus == 0 || bound_cpus > 1) {
252  return std::make_pair(false, static_cast<unsigned>(0));
253  } else {
254  return std::make_pair(true, candidate);
255  }
256 #else
257  piranha_throw(not_implemented_error, "bound_proc() is not available on this platform");
258 #endif
259 }
260 }
261 
262 #endif
void bind_to_proc(unsigned n)
Bind thread to specific processor.
Exceptions.
std::pair< bool, unsigned > bound_proc()
Query if current thread is bound to a processor.
#define piranha_throw(exception_type,...)
Exception-throwing macro.
Definition: exceptions.hpp:118
Root piranha namespace.
Definition: array_key.hpp:52
Exception for functionality not implemented or not available on the current platform.
Definition: exceptions.hpp:128
static unsigned get_hardware_concurrency()
Hardware concurrency.
Exception to signal failure in piranha::safe_cast().
Definition: safe_cast.hpp:53
safe_cast_type< To, From > safe_cast(const From &x)
Safe cast.
Definition: safe_cast.hpp:219