(root)/
coreutils-9.4/
lib/
nproc.c
       1  /* Detect the number of processors.
       2  
       3     Copyright (C) 2009-2023 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__ && defined __linux__)
      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)
      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)
     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 __linux__) && 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)
     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)
     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  }