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 Mac OS X, FreeBSD, NetBSD, OpenBSD.  */
 311     int nprocs;
 312     size_t len = sizeof (nprocs);
 313     static int const mib[][2] = {
 314 # ifdef HW_NCPUONLINE
 315       { CTL_HW, HW_NCPUONLINE },
 316 # endif
 317       { CTL_HW, HW_NCPU }
 318     };
 319     for (int i = 0; i < ARRAY_SIZE (mib); i++)
 320       {
 321         if (sysctl (mib[i], ARRAY_SIZE (mib[i]), &nprocs, &len, NULL, 0) == 0
 322             && len == sizeof (nprocs)
 323             && 0 < nprocs)
 324           return nprocs;
 325       }
 326   }
 327 #endif
 328 
 329 #if defined _WIN32 && ! defined __CYGWIN__
 330   { /* This works on native Windows platforms.  */
 331     SYSTEM_INFO system_info;
 332     GetSystemInfo (&system_info);
 333     if (0 < system_info.dwNumberOfProcessors)
 334       return system_info.dwNumberOfProcessors;
 335   }
 336 #endif
 337 
 338   return 1;
 339 }
 340 
 341 /* Parse OMP environment variables without dependence on OMP.
 342    Return 0 for invalid values.  */
 343 static unsigned long int
 344 parse_omp_threads (char const* threads)
     /* [previous][next][first][last][top][bottom][index][help] */
 345 {
 346   unsigned long int ret = 0;
 347 
 348   if (threads == NULL)
 349     return ret;
 350 
 351   /* The OpenMP spec says that the value assigned to the environment variables
 352      "may have leading and trailing white space".  */
 353   while (*threads != '\0' && c_isspace (*threads))
 354     threads++;
 355 
 356   /* Convert it from positive decimal to 'unsigned long'.  */
 357   if (c_isdigit (*threads))
 358     {
 359       char *endptr = NULL;
 360       unsigned long int value = strtoul (threads, &endptr, 10);
 361 
 362       if (endptr != NULL)
 363         {
 364           while (*endptr != '\0' && c_isspace (*endptr))
 365             endptr++;
 366           if (*endptr == '\0')
 367             return value;
 368           /* Also accept the first value in a nesting level,
 369              since we can't determine the nesting level from env vars.  */
 370           else if (*endptr == ',')
 371             return value;
 372         }
 373     }
 374 
 375   return ret;
 376 }
 377 
 378 unsigned long int
 379 num_processors (enum nproc_query query)
     /* [previous][next][first][last][top][bottom][index][help] */
 380 {
 381   unsigned long int omp_env_limit = ULONG_MAX;
 382 
 383   if (query == NPROC_CURRENT_OVERRIDABLE)
 384     {
 385       unsigned long int omp_env_threads;
 386       /* Honor the OpenMP environment variables, recognized also by all
 387          programs that are based on OpenMP.  */
 388       omp_env_threads = parse_omp_threads (getenv ("OMP_NUM_THREADS"));
 389       omp_env_limit = parse_omp_threads (getenv ("OMP_THREAD_LIMIT"));
 390       if (! omp_env_limit)
 391         omp_env_limit = ULONG_MAX;
 392 
 393       if (omp_env_threads)
 394         return MIN (omp_env_threads, omp_env_limit);
 395 
 396       query = NPROC_CURRENT;
 397     }
 398   /* Here query is one of NPROC_ALL, NPROC_CURRENT.  */
 399   {
 400     unsigned long nprocs = num_processors_ignoring_omp (query);
 401     return MIN (nprocs, omp_env_limit);
 402   }
 403 }

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