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