maint: update copyright
[gnulib.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 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 General Public License for more details.
14
15    You should have received a copy of the GNU 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 }