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