root/maint/gnulib/lib/nproc.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. num_processors_via_affinity_mask
  2. num_processors_ignoring_omp
  3. parse_omp_threads
  4. num_processors

   1 /* Detect the number of processors.
   2 
   3    Copyright (C) 2009-2021 Free Software Foundation, Inc.
   4 
   5    This file is free software: you can redistribute it and/or modify
   6    it under the terms of the GNU Lesser General Public License as
   7    published by the Free Software Foundation; either version 2.1 of the
   8    License, or (at your option) any later version.
   9 
  10    This file 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 <https://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 <limits.h>
  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 && ! defined __GLIBC__
  50 # include <sys/sysctl.h>
  51 #endif
  52 
  53 #if defined _WIN32 && ! defined __CYGWIN__
  54 # define WIN32_LEAN_AND_MEAN
  55 # include <windows.h>
  56 #endif
  57 
  58 #include "c-ctype.h"
  59 
  60 #include "minmax.h"
  61 
  62 #define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
  63 
  64 /* Return the number of processors available to the current process, based
  65    on a modern system call that returns the "affinity" between the current
  66    process and each CPU.  Return 0 if unknown or if such a system call does
  67    not exist.  */
  68 static unsigned long
  69 num_processors_via_affinity_mask (void)
     /* [previous][next][first][last][top][bottom][index][help] */
  70 {
  71   /* glibc >= 2.3.3 with NPTL and NetBSD 5 have pthread_getaffinity_np,
  72      but with different APIs.  Also it requires linking with -lpthread.
  73      Therefore this code is not enabled.
  74      glibc >= 2.3.4 has sched_getaffinity whereas NetBSD 5 has
  75      sched_getaffinity_np.  */
  76 #if HAVE_PTHREAD_GETAFFINITY_NP && defined __GLIBC__ && 0
  77   {
  78     cpu_set_t set;
  79 
  80     if (pthread_getaffinity_np (pthread_self (), sizeof (set), &set) == 0)
  81       {
  82         unsigned long count;
  83 
  84 # ifdef CPU_COUNT
  85         /* glibc >= 2.6 has the CPU_COUNT macro.  */
  86         count = CPU_COUNT (&set);
  87 # else
  88         size_t i;
  89 
  90         count = 0;
  91         for (i = 0; i < CPU_SETSIZE; i++)
  92           if (CPU_ISSET (i, &set))
  93             count++;
  94 # endif
  95         if (count > 0)
  96           return count;
  97       }
  98   }
  99 #elif HAVE_PTHREAD_GETAFFINITY_NP && defined __NetBSD__ && 0
 100   {
 101     cpuset_t *set;
 102 
 103     set = cpuset_create ();
 104     if (set != NULL)
 105       {
 106         unsigned long count = 0;
 107 
 108         if (pthread_getaffinity_np (pthread_self (), cpuset_size (set), set)
 109             == 0)
 110           {
 111             cpuid_t i;
 112 
 113             for (i = 0;; i++)
 114               {
 115                 int ret = cpuset_isset (i, set);
 116                 if (ret < 0)
 117                   break;
 118                 if (ret > 0)
 119                   count++;
 120               }
 121           }
 122         cpuset_destroy (set);
 123         if (count > 0)
 124           return count;
 125       }
 126   }
 127 #elif HAVE_SCHED_GETAFFINITY_LIKE_GLIBC /* glibc >= 2.3.4 */
 128   {
 129     cpu_set_t set;
 130 
 131     if (sched_getaffinity (0, sizeof (set), &set) == 0)
 132       {
 133         unsigned long count;
 134 
 135 # ifdef CPU_COUNT
 136         /* glibc >= 2.6 has the CPU_COUNT macro.  */
 137         count = CPU_COUNT (&set);
 138 # else
 139         size_t i;
 140 
 141         count = 0;
 142         for (i = 0; i < CPU_SETSIZE; i++)
 143           if (CPU_ISSET (i, &set))
 144             count++;
 145 # endif
 146         if (count > 0)
 147           return count;
 148       }
 149   }
 150 #elif HAVE_SCHED_GETAFFINITY_NP /* NetBSD >= 5 */
 151   {
 152     cpuset_t *set;
 153 
 154     set = cpuset_create ();
 155     if (set != NULL)
 156       {
 157         unsigned long count = 0;
 158 
 159         if (sched_getaffinity_np (getpid (), cpuset_size (set), set) == 0)
 160           {
 161             cpuid_t i;
 162 
 163             for (i = 0;; i++)
 164               {
 165                 int ret = cpuset_isset (i, set);
 166                 if (ret < 0)
 167                   break;
 168                 if (ret > 0)
 169                   count++;
 170               }
 171           }
 172         cpuset_destroy (set);
 173         if (count > 0)
 174           return count;
 175       }
 176   }
 177 #endif
 178 
 179 #if defined _WIN32 && ! defined __CYGWIN__
 180   { /* This works on native Windows platforms.  */
 181     DWORD_PTR process_mask;
 182     DWORD_PTR system_mask;
 183 
 184     if (GetProcessAffinityMask (GetCurrentProcess (),
 185                                 &process_mask, &system_mask))
 186       {
 187         DWORD_PTR mask = process_mask;
 188         unsigned long count = 0;
 189 
 190         for (; mask != 0; mask = mask >> 1)
 191           if (mask & 1)
 192             count++;
 193         if (count > 0)
 194           return count;
 195       }
 196   }
 197 #endif
 198 
 199   return 0;
 200 }
 201 
 202 
 203 /* Return the total number of processors.  Here QUERY must be one of
 204    NPROC_ALL, NPROC_CURRENT.  The result is guaranteed to be at least 1.  */
 205 static unsigned long int
 206 num_processors_ignoring_omp (enum nproc_query query)
     /* [previous][next][first][last][top][bottom][index][help] */
 207 {
 208   /* On systems with a modern affinity mask system call, we have
 209          sysconf (_SC_NPROCESSORS_CONF)
 210             >= sysconf (_SC_NPROCESSORS_ONLN)
 211                >= num_processors_via_affinity_mask ()
 212      The first number is the number of CPUs configured in the system.
 213      The second number is the number of CPUs available to the scheduler.
 214      The third number is the number of CPUs available to the current process.
 215 
 216      Note! On Linux systems with glibc, the first and second number come from
 217      the /sys and /proc file systems (see
 218      glibc/sysdeps/unix/sysv/linux/getsysstats.c).
 219      In some situations these file systems are not mounted, and the sysconf call
 220      returns 1 or 2 (<https://sourceware.org/bugzilla/show_bug.cgi?id=21542>),
 221      which does not reflect the reality.  */
 222 
 223   if (query == NPROC_CURRENT)
 224     {
 225       /* Try the modern affinity mask system call.  */
 226       {
 227         unsigned long nprocs = num_processors_via_affinity_mask ();
 228 
 229         if (nprocs > 0)
 230           return nprocs;
 231       }
 232 
 233 #if defined _SC_NPROCESSORS_ONLN
 234       { /* This works on glibc, Mac OS X 10.5, FreeBSD, AIX, OSF/1, Solaris,
 235            Cygwin, Haiku.  */
 236         long int nprocs = sysconf (_SC_NPROCESSORS_ONLN);
 237         if (nprocs > 0)
 238           return nprocs;
 239       }
 240 #endif
 241     }
 242   else /* query == NPROC_ALL */
 243     {
 244 #if defined _SC_NPROCESSORS_CONF
 245       { /* This works on glibc, Mac OS X 10.5, FreeBSD, AIX, OSF/1, Solaris,
 246            Cygwin, Haiku.  */
 247         long int nprocs = sysconf (_SC_NPROCESSORS_CONF);
 248 
 249 # if __GLIBC__ >= 2 && defined __linux__
 250         /* On Linux systems with glibc, this information comes from the /sys and
 251            /proc file systems (see glibc/sysdeps/unix/sysv/linux/getsysstats.c).
 252            In some situations these file systems are not mounted, and the
 253            sysconf call returns 1 or 2.  But we wish to guarantee that
 254            num_processors (NPROC_ALL) >= num_processors (NPROC_CURRENT).  */
 255         if (nprocs == 1 || nprocs == 2)
 256           {
 257             unsigned long nprocs_current = num_processors_via_affinity_mask ();
 258 
 259             if (/* nprocs_current > 0 && */ nprocs_current > nprocs)
 260               nprocs = nprocs_current;
 261           }
 262 # endif
 263 
 264         if (nprocs > 0)
 265           return nprocs;
 266       }
 267 #endif
 268     }
 269 
 270 #if HAVE_PSTAT_GETDYNAMIC
 271   { /* This works on HP-UX.  */
 272     struct pst_dynamic psd;
 273     if (pstat_getdynamic (&psd, sizeof psd, 1, 0) >= 0)
 274       {
 275         /* The field psd_proc_cnt contains the number of active processors.
 276            In newer releases of HP-UX 11, the field psd_max_proc_cnt includes
 277            deactivated processors.  */
 278         if (query == NPROC_CURRENT)
 279           {
 280             if (psd.psd_proc_cnt > 0)
 281               return psd.psd_proc_cnt;
 282           }
 283         else
 284           {
 285             if (psd.psd_max_proc_cnt > 0)
 286               return psd.psd_max_proc_cnt;
 287           }
 288       }
 289   }
 290 #endif
 291 
 292 #if HAVE_SYSMP && defined MP_NAPROCS && defined MP_NPROCS
 293   { /* This works on IRIX.  */
 294     /* MP_NPROCS yields the number of installed processors.
 295        MP_NAPROCS yields the number of processors available to unprivileged
 296        processes.  */
 297     int nprocs =
 298       sysmp (query == NPROC_CURRENT && getuid () != 0
 299              ? MP_NAPROCS
 300              : MP_NPROCS);
 301     if (nprocs > 0)
 302       return nprocs;
 303   }
 304 #endif
 305 
 306   /* Finally, as fallback, use the APIs that don't distinguish between
 307      NPROC_CURRENT and NPROC_ALL.  */
 308 
 309 #if HAVE_SYSCTL && ! defined __GLIBC__ && defined HW_NCPU
 310   { /* This works on macOS, FreeBSD, NetBSD, OpenBSD.
 311        macOS 10.14 does not allow mib to be const.  */
 312     int nprocs;
 313     size_t len = sizeof (nprocs);
 314     static int mib[][2] = {
 315 # ifdef HW_NCPUONLINE
 316       { CTL_HW, HW_NCPUONLINE },
 317 # endif
 318       { CTL_HW, HW_NCPU }
 319     };
 320     for (int i = 0; i < ARRAY_SIZE (mib); i++)
 321       {
 322         if (sysctl (mib[i], ARRAY_SIZE (mib[i]), &nprocs, &len, NULL, 0) == 0
 323             && len == sizeof (nprocs)
 324             && 0 < nprocs)
 325           return nprocs;
 326       }
 327   }
 328 #endif
 329 
 330 #if defined _WIN32 && ! defined __CYGWIN__
 331   { /* This works on native Windows platforms.  */
 332     SYSTEM_INFO system_info;
 333     GetSystemInfo (&system_info);
 334     if (0 < system_info.dwNumberOfProcessors)
 335       return system_info.dwNumberOfProcessors;
 336   }
 337 #endif
 338 
 339   return 1;
 340 }
 341 
 342 /* Parse OMP environment variables without dependence on OMP.
 343    Return 0 for invalid values.  */
 344 static unsigned long int
 345 parse_omp_threads (char const* threads)
     /* [previous][next][first][last][top][bottom][index][help] */
 346 {
 347   unsigned long int ret = 0;
 348 
 349   if (threads == NULL)
 350     return ret;
 351 
 352   /* The OpenMP spec says that the value assigned to the environment variables
 353      "may have leading and trailing white space".  */
 354   while (*threads != '\0' && c_isspace (*threads))
 355     threads++;
 356 
 357   /* Convert it from positive decimal to 'unsigned long'.  */
 358   if (c_isdigit (*threads))
 359     {
 360       char *endptr = NULL;
 361       unsigned long int value = strtoul (threads, &endptr, 10);
 362 
 363       if (endptr != NULL)
 364         {
 365           while (*endptr != '\0' && c_isspace (*endptr))
 366             endptr++;
 367           if (*endptr == '\0')
 368             return value;
 369           /* Also accept the first value in a nesting level,
 370              since we can't determine the nesting level from env vars.  */
 371           else if (*endptr == ',')
 372             return value;
 373         }
 374     }
 375 
 376   return ret;
 377 }
 378 
 379 unsigned long int
 380 num_processors (enum nproc_query query)
     /* [previous][next][first][last][top][bottom][index][help] */
 381 {
 382   unsigned long int omp_env_limit = ULONG_MAX;
 383 
 384   if (query == NPROC_CURRENT_OVERRIDABLE)
 385     {
 386       unsigned long int omp_env_threads;
 387       /* Honor the OpenMP environment variables, recognized also by all
 388          programs that are based on OpenMP.  */
 389       omp_env_threads = parse_omp_threads (getenv ("OMP_NUM_THREADS"));
 390       omp_env_limit = parse_omp_threads (getenv ("OMP_THREAD_LIMIT"));
 391       if (! omp_env_limit)
 392         omp_env_limit = ULONG_MAX;
 393 
 394       if (omp_env_threads)
 395         return MIN (omp_env_threads, omp_env_limit);
 396 
 397       query = NPROC_CURRENT;
 398     }
 399   /* Here query is one of NPROC_ALL, NPROC_CURRENT.  */
 400   {
 401     unsigned long nprocs = num_processors_ignoring_omp (query);
 402     return MIN (nprocs, omp_env_limit);
 403   }
 404 }

/* [previous][next][first][last][top][bottom][index][help] */