temporarily disable elisp exception tests
[bpt/guile.git] / lib / nproc.c
1 /* Detect the number of processors.
2
3 Copyright (C) 2009-2014 Free Software Foundation, Inc.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program; if not, see <http://www.gnu.org/licenses/>. */
17
18 /* Written by Glen Lenker and Bruno Haible. */
19
20 #include <config.h>
21 #include "nproc.h"
22
23 #include <stdlib.h>
24 #include <unistd.h>
25
26 #if HAVE_PTHREAD_GETAFFINITY_NP && 0
27 # include <pthread.h>
28 # include <sched.h>
29 #endif
30 #if HAVE_SCHED_GETAFFINITY_LIKE_GLIBC || HAVE_SCHED_GETAFFINITY_NP
31 # include <sched.h>
32 #endif
33
34 #include <sys/types.h>
35
36 #if HAVE_SYS_PSTAT_H
37 # include <sys/pstat.h>
38 #endif
39
40 #if HAVE_SYS_SYSMP_H
41 # include <sys/sysmp.h>
42 #endif
43
44 #if HAVE_SYS_PARAM_H
45 # include <sys/param.h>
46 #endif
47
48 #if HAVE_SYS_SYSCTL_H
49 # include <sys/sysctl.h>
50 #endif
51
52 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
53 # define WIN32_LEAN_AND_MEAN
54 # include <windows.h>
55 #endif
56
57 #include "c-ctype.h"
58
59 #define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
60
61 /* Return the number of processors available to the current process, based
62 on a modern system call that returns the "affinity" between the current
63 process and each CPU. Return 0 if unknown or if such a system call does
64 not exist. */
65 static unsigned long
66 num_processors_via_affinity_mask (void)
67 {
68 /* glibc >= 2.3.3 with NPTL and NetBSD 5 have pthread_getaffinity_np,
69 but with different APIs. Also it requires linking with -lpthread.
70 Therefore this code is not enabled.
71 glibc >= 2.3.4 has sched_getaffinity whereas NetBSD 5 has
72 sched_getaffinity_np. */
73 #if HAVE_PTHREAD_GETAFFINITY_NP && defined __GLIBC__ && 0
74 {
75 cpu_set_t set;
76
77 if (pthread_getaffinity_np (pthread_self (), sizeof (set), &set) == 0)
78 {
79 unsigned long count;
80
81 # ifdef CPU_COUNT
82 /* glibc >= 2.6 has the CPU_COUNT macro. */
83 count = CPU_COUNT (&set);
84 # else
85 size_t i;
86
87 count = 0;
88 for (i = 0; i < CPU_SETSIZE; i++)
89 if (CPU_ISSET (i, &set))
90 count++;
91 # endif
92 if (count > 0)
93 return count;
94 }
95 }
96 #elif HAVE_PTHREAD_GETAFFINITY_NP && defined __NetBSD__ && 0
97 {
98 cpuset_t *set;
99
100 set = cpuset_create ();
101 if (set != NULL)
102 {
103 unsigned long count = 0;
104
105 if (pthread_getaffinity_np (pthread_self (), cpuset_size (set), set)
106 == 0)
107 {
108 cpuid_t i;
109
110 for (i = 0;; i++)
111 {
112 int ret = cpuset_isset (i, set);
113 if (ret < 0)
114 break;
115 if (ret > 0)
116 count++;
117 }
118 }
119 cpuset_destroy (set);
120 if (count > 0)
121 return count;
122 }
123 }
124 #elif HAVE_SCHED_GETAFFINITY_LIKE_GLIBC /* glibc >= 2.3.4 */
125 {
126 cpu_set_t set;
127
128 if (sched_getaffinity (0, sizeof (set), &set) == 0)
129 {
130 unsigned long count;
131
132 # ifdef CPU_COUNT
133 /* glibc >= 2.6 has the CPU_COUNT macro. */
134 count = CPU_COUNT (&set);
135 # else
136 size_t i;
137
138 count = 0;
139 for (i = 0; i < CPU_SETSIZE; i++)
140 if (CPU_ISSET (i, &set))
141 count++;
142 # endif
143 if (count > 0)
144 return count;
145 }
146 }
147 #elif HAVE_SCHED_GETAFFINITY_NP /* NetBSD >= 5 */
148 {
149 cpuset_t *set;
150
151 set = cpuset_create ();
152 if (set != NULL)
153 {
154 unsigned long count = 0;
155
156 if (sched_getaffinity_np (getpid (), cpuset_size (set), set) == 0)
157 {
158 cpuid_t i;
159
160 for (i = 0;; i++)
161 {
162 int ret = cpuset_isset (i, set);
163 if (ret < 0)
164 break;
165 if (ret > 0)
166 count++;
167 }
168 }
169 cpuset_destroy (set);
170 if (count > 0)
171 return count;
172 }
173 }
174 #endif
175
176 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
177 { /* This works on native Windows platforms. */
178 DWORD_PTR process_mask;
179 DWORD_PTR system_mask;
180
181 if (GetProcessAffinityMask (GetCurrentProcess (),
182 &process_mask, &system_mask))
183 {
184 DWORD_PTR mask = process_mask;
185 unsigned long count = 0;
186
187 for (; mask != 0; mask = mask >> 1)
188 if (mask & 1)
189 count++;
190 if (count > 0)
191 return count;
192 }
193 }
194 #endif
195
196 return 0;
197 }
198
199 unsigned long int
200 num_processors (enum nproc_query query)
201 {
202 if (query == NPROC_CURRENT_OVERRIDABLE)
203 {
204 /* Test the environment variable OMP_NUM_THREADS, recognized also by all
205 programs that are based on OpenMP. The OpenMP spec says that the
206 value assigned to the environment variable "may have leading and
207 trailing white space". */
208 const char *envvalue = getenv ("OMP_NUM_THREADS");
209
210 if (envvalue != NULL)
211 {
212 while (*envvalue != '\0' && c_isspace (*envvalue))
213 envvalue++;
214 /* Convert it from decimal to 'unsigned long'. */
215 if (c_isdigit (*envvalue))
216 {
217 char *endptr = NULL;
218 unsigned long int value = strtoul (envvalue, &endptr, 10);
219
220 if (endptr != NULL)
221 {
222 while (*endptr != '\0' && c_isspace (*endptr))
223 endptr++;
224 if (*endptr == '\0')
225 return (value > 0 ? value : 1);
226 }
227 }
228 }
229
230 query = NPROC_CURRENT;
231 }
232 /* Here query is one of NPROC_ALL, NPROC_CURRENT. */
233
234 /* On systems with a modern affinity mask system call, we have
235 sysconf (_SC_NPROCESSORS_CONF)
236 >= sysconf (_SC_NPROCESSORS_ONLN)
237 >= num_processors_via_affinity_mask ()
238 The first number is the number of CPUs configured in the system.
239 The second number is the number of CPUs available to the scheduler.
240 The third number is the number of CPUs available to the current process.
241
242 Note! On Linux systems with glibc, the first and second number come from
243 the /sys and /proc file systems (see
244 glibc/sysdeps/unix/sysv/linux/getsysstats.c).
245 In some situations these file systems are not mounted, and the sysconf
246 call returns 1, which does not reflect the reality. */
247
248 if (query == NPROC_CURRENT)
249 {
250 /* Try the modern affinity mask system call. */
251 {
252 unsigned long nprocs = num_processors_via_affinity_mask ();
253
254 if (nprocs > 0)
255 return nprocs;
256 }
257
258 #if defined _SC_NPROCESSORS_ONLN
259 { /* This works on glibc, Mac OS X 10.5, FreeBSD, AIX, OSF/1, Solaris,
260 Cygwin, Haiku. */
261 long int nprocs = sysconf (_SC_NPROCESSORS_ONLN);
262 if (nprocs > 0)
263 return nprocs;
264 }
265 #endif
266 }
267 else /* query == NPROC_ALL */
268 {
269 #if defined _SC_NPROCESSORS_CONF
270 { /* This works on glibc, Mac OS X 10.5, FreeBSD, AIX, OSF/1, Solaris,
271 Cygwin, Haiku. */
272 long int nprocs = sysconf (_SC_NPROCESSORS_CONF);
273
274 # if __GLIBC__ >= 2 && defined __linux__
275 /* On Linux systems with glibc, this information comes from the /sys and
276 /proc file systems (see glibc/sysdeps/unix/sysv/linux/getsysstats.c).
277 In some situations these file systems are not mounted, and the
278 sysconf call returns 1. But we wish to guarantee that
279 num_processors (NPROC_ALL) >= num_processors (NPROC_CURRENT). */
280 if (nprocs == 1)
281 {
282 unsigned long nprocs_current = num_processors_via_affinity_mask ();
283
284 if (nprocs_current > 0)
285 nprocs = nprocs_current;
286 }
287 # endif
288
289 if (nprocs > 0)
290 return nprocs;
291 }
292 #endif
293 }
294
295 #if HAVE_PSTAT_GETDYNAMIC
296 { /* This works on HP-UX. */
297 struct pst_dynamic psd;
298 if (pstat_getdynamic (&psd, sizeof psd, 1, 0) >= 0)
299 {
300 /* The field psd_proc_cnt contains the number of active processors.
301 In newer releases of HP-UX 11, the field psd_max_proc_cnt includes
302 deactivated processors. */
303 if (query == NPROC_CURRENT)
304 {
305 if (psd.psd_proc_cnt > 0)
306 return psd.psd_proc_cnt;
307 }
308 else
309 {
310 if (psd.psd_max_proc_cnt > 0)
311 return psd.psd_max_proc_cnt;
312 }
313 }
314 }
315 #endif
316
317 #if HAVE_SYSMP && defined MP_NAPROCS && defined MP_NPROCS
318 { /* This works on IRIX. */
319 /* MP_NPROCS yields the number of installed processors.
320 MP_NAPROCS yields the number of processors available to unprivileged
321 processes. */
322 int nprocs =
323 sysmp (query == NPROC_CURRENT && getpid () != 0
324 ? MP_NAPROCS
325 : MP_NPROCS);
326 if (nprocs > 0)
327 return nprocs;
328 }
329 #endif
330
331 /* Finally, as fallback, use the APIs that don't distinguish between
332 NPROC_CURRENT and NPROC_ALL. */
333
334 #if HAVE_SYSCTL && defined HW_NCPU
335 { /* This works on Mac OS X, FreeBSD, NetBSD, OpenBSD. */
336 int nprocs;
337 size_t len = sizeof (nprocs);
338 static int mib[2] = { CTL_HW, HW_NCPU };
339
340 if (sysctl (mib, ARRAY_SIZE (mib), &nprocs, &len, NULL, 0) == 0
341 && len == sizeof (nprocs)
342 && 0 < nprocs)
343 return nprocs;
344 }
345 #endif
346
347 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
348 { /* This works on native Windows platforms. */
349 SYSTEM_INFO system_info;
350 GetSystemInfo (&system_info);
351 if (0 < system_info.dwNumberOfProcessors)
352 return system_info.dwNumberOfProcessors;
353 }
354 #endif
355
356 return 1;
357 }