bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: I: coreutils tests/misc/nproc-avail fails on GNU/Linux without /proc


From: Bruno Haible
Subject: Re: I: coreutils tests/misc/nproc-avail fails on GNU/Linux without /proc and /sys mounted
Date: Sun, 10 Jan 2010 11:32:50 +0100
User-agent: KMail/1.9.9

Pádraig Brady wrote:
> Just to summarize what's happening here...
> 
> There are 3 CPU counts possible:
> 
> total >= online >= available
> 
> "online" corresponds to the CPUs enabled system wide,
> whereas "available" is what's available to a particular
> process which may be less due to affinity config.
> 
> "online" is not currently exposed through nproc
> but for reference is got on linux using:
> $ strace -e open getconf _NPROCESSORS_ONLN
>    open("/proc/stat"
>      for_each_online_cpu() //available to scheduler
> 
> "available" on linux is determined using:
> nproc
>    glibc::sched_getaffinity()
>      return corresponding_syscall();
> 
> "total" on linux is determined using:
> nproc --all
>    glibc::sysconf(_NPROCESSORS_CONF)
>      __get_nprocs_conf ()
>      {
>        int result = 1;
>        /* XXX Here will come a test for the new system call.  */
>        if (open("/sys/devices/system/cpu"))
>          result = parse();
>        else if (open("/proc/cpuinfo"))
>          result = parse();
>        return result;
>      }
> 
> The last one above is giving the issue as
> it relies on /sys or /proc being available.

Ah! Thanks for the detailed explanation.

> So what about a possible work around?
> How about doing this in nproc(1):
> 
> if (num_processors(NPROC_ALL) == 1)
>      return num_processors(NPROC_CURRENT_OVERRIDABLE)

It should be NPROC_CURRENT, not NPROC_CURRENT_OVERRIDABLE, I think,
because NPROC_CURRENT_OVERRIDABLE can be overridden by the user quite
arbitrarily.

I like this workaround, but would prefer to have it in the nproc module
in gnulib. coreutils/src/nproc.c is purely the command-line program.

Here's a proposed patch.


2010-01-10  Bruno Haible  <address@hidden>

        nproc: Work better on Linux when /proc and /sys are not mounted.
        * lib/nproc.c (num_processors_via_affinity_mask): New function,
        extracted from num_processors.
        (num_processors): Call it. Use it as lower bound when, on glibc/Linux
        systems, sysconf (_SC_NPROCESSORS_CONF) returns 1.
        Suggested by Pádraig Brady <address@hidden>.
        Reported by Dmitry V. Levin <address@hidden>.

*** lib/nproc.c.orig    Sun Jan 10 11:24:18 2010
--- lib/nproc.c Sun Jan 10 11:21:40 2010
***************
*** 59,229 ****
  
  #define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
  
! unsigned long int
! num_processors (enum nproc_query query)
  {
!   if (query == NPROC_CURRENT_OVERRIDABLE)
!     {
!       /* Test the environment variable OMP_NUM_THREADS, recognized also by all
!          programs that are based on OpenMP.  The OpenMP spec says that the
!          value assigned to the environment variable "may have leading and
!          trailing white space". */
!       const char *envvalue = getenv ("OMP_NUM_THREADS");
! 
!       if (envvalue != NULL)
!         {
!           while (*envvalue != '\0' && c_isspace (*envvalue))
!             envvalue++;
!           /* Convert it from decimal to 'unsigned long'.  */
!           if (c_isdigit (*envvalue))
!             {
!               char *endptr = NULL;
!               unsigned long int value = strtoul (envvalue, &endptr, 10);
! 
!               if (endptr != NULL)
!                 {
!                   while (*endptr != '\0' && c_isspace (*endptr))
!                     endptr++;
!                   if (*endptr == '\0')
!                     return (value > 0 ? value : 1);
!                 }
!             }
!         }
! 
!       query = NPROC_CURRENT;
!     }
!   /* Here query is one of NPROC_ALL, NPROC_CURRENT.  */
! 
!   if (query == NPROC_CURRENT)
!     {
!       /* glibc >= 2.3.3 with NPTL and NetBSD 5 have pthread_getaffinity_np,
!          but with different APIs.  Also it requires linking with -lpthread.
!          Therefore this code is not enabled.
!          glibc >= 2.3.4 has sched_getaffinity whereas NetBSD 5 has
!          sched_getaffinity_np.  */
  #if HAVE_PTHREAD_AFFINITY_NP && defined __GLIBC__ && 0
!       {
!         cpu_set_t set;
  
!         if (pthread_getaffinity_np (pthread_self (), sizeof (set), &set) == 0)
!           {
!             unsigned long count;
  
  # ifdef CPU_COUNT
!             /* glibc >= 2.6 has the CPU_COUNT macro.  */
!             count = CPU_COUNT (&set);
  # else
!             size_t i;
  
!             count = 0;
!             for (i = 0; i < CPU_SETSIZE; i++)
!               if (CPU_ISSET (i, &set))
!                 count++;
  # endif
!             if (count > 0)
!               return count;
!           }
        }
  #elif HAVE_PTHREAD_AFFINITY_NP && defined __NetBSD__ && 0
        {
!         cpuset_t *set;
  
!         set = cpuset_create ();
!         if (set != NULL)
            {
!             unsigned long count = 0;
  
!             if (pthread_getaffinity_np (pthread_self (), cpuset_size (set), 
set)
!                 == 0)
                {
!                 cpuid_t i;
! 
!                 for (i = 0;; i++)
!                   {
!                     int ret = cpuset_isset (i, set);
!                     if (ret < 0)
!                       break;
!                     if (ret > 0)
!                       count++;
!                   }
                }
-             cpuset_destroy (set);
-             if (count > 0)
-               return count;
            }
        }
  #elif HAVE_SCHED_GETAFFINITY_LIKE_GLIBC /* glibc >= 2.3.4 */
!       {
!         cpu_set_t set;
  
!         if (sched_getaffinity (0, sizeof (set), &set) == 0)
!           {
!             unsigned long count;
  
  # ifdef CPU_COUNT
!             /* glibc >= 2.6 has the CPU_COUNT macro.  */
!             count = CPU_COUNT (&set);
  # else
!             size_t i;
  
!             count = 0;
!             for (i = 0; i < CPU_SETSIZE; i++)
!               if (CPU_ISSET (i, &set))
!                 count++;
  # endif
!             if (count > 0)
!               return count;
!           }
        }
  #elif HAVE_SCHED_GETAFFINITY_NP /* NetBSD >= 5 */
        {
!         cpuset_t *set;
  
!         set = cpuset_create ();
!         if (set != NULL)
            {
!             unsigned long count = 0;
  
!             if (sched_getaffinity_np (getpid (), cpuset_size (set), set) == 0)
                {
!                 cpuid_t i;
! 
!                 for (i = 0;; i++)
!                   {
!                     int ret = cpuset_isset (i, set);
!                     if (ret < 0)
!                       break;
!                     if (ret > 0)
!                       count++;
!                   }
                }
-             cpuset_destroy (set);
-             if (count > 0)
-               return count;
            }
        }
  #endif
  
  #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
!       { /* This works on native Windows platforms.  */
!         DWORD_PTR process_mask;
!         DWORD_PTR system_mask;
  
!         if (GetProcessAffinityMask (GetCurrentProcess (),
!                                     &process_mask, &system_mask))
!           {
!             DWORD_PTR mask = process_mask;
!             unsigned long count = 0;
  
!             for (; mask != 0; mask = mask >> 1)
!               if (mask & 1)
!                 count++;
!             if (count > 0)
!               return count;
!           }
        }
  #endif
  
  #if defined _SC_NPROCESSORS_ONLN
        { /* This works on glibc, MacOS X 10.5, FreeBSD, AIX, OSF/1, Solaris,
             Cygwin, Haiku.  */
--- 59,261 ----
  
  #define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
  
! /* Return the number of processors available to the current process, based
!    on a modern system call that returns the "affinity" between the current
!    process and each CPU.  Return 0 if unknown or if such a system call does
!    not exist.  */
! static unsigned long
! num_processors_via_affinity_mask (void)
  {
!   /* glibc >= 2.3.3 with NPTL and NetBSD 5 have pthread_getaffinity_np,
!      but with different APIs.  Also it requires linking with -lpthread.
!      Therefore this code is not enabled.
!      glibc >= 2.3.4 has sched_getaffinity whereas NetBSD 5 has
!      sched_getaffinity_np.  */
  #if HAVE_PTHREAD_AFFINITY_NP && defined __GLIBC__ && 0
!   {
!     cpu_set_t set;
  
!     if (pthread_getaffinity_np (pthread_self (), sizeof (set), &set) == 0)
!       {
!         unsigned long count;
  
  # ifdef CPU_COUNT
!         /* glibc >= 2.6 has the CPU_COUNT macro.  */
!         count = CPU_COUNT (&set);
  # else
!         size_t i;
  
!         count = 0;
!         for (i = 0; i < CPU_SETSIZE; i++)
!           if (CPU_ISSET (i, &set))
!             count++;
  # endif
!         if (count > 0)
!           return count;
        }
+   }
  #elif HAVE_PTHREAD_AFFINITY_NP && defined __NetBSD__ && 0
+   {
+     cpuset_t *set;
+ 
+     set = cpuset_create ();
+     if (set != NULL)
        {
!         unsigned long count = 0;
  
!         if (pthread_getaffinity_np (pthread_self (), cpuset_size (set), set)
!             == 0)
            {
!             cpuid_t i;
  
!             for (i = 0;; i++)
                {
!                 int ret = cpuset_isset (i, set);
!                 if (ret < 0)
!                   break;
!                 if (ret > 0)
!                   count++;
                }
            }
+         cpuset_destroy (set);
+         if (count > 0)
+           return count;
        }
+   }
  #elif HAVE_SCHED_GETAFFINITY_LIKE_GLIBC /* glibc >= 2.3.4 */
!   {
!     cpu_set_t set;
  
!     if (sched_getaffinity (0, sizeof (set), &set) == 0)
!       {
!         unsigned long count;
  
  # ifdef CPU_COUNT
!         /* glibc >= 2.6 has the CPU_COUNT macro.  */
!         count = CPU_COUNT (&set);
  # else
!         size_t i;
  
!         count = 0;
!         for (i = 0; i < CPU_SETSIZE; i++)
!           if (CPU_ISSET (i, &set))
!             count++;
  # endif
!         if (count > 0)
!           return count;
        }
+   }
  #elif HAVE_SCHED_GETAFFINITY_NP /* NetBSD >= 5 */
+   {
+     cpuset_t *set;
+ 
+     set = cpuset_create ();
+     if (set != NULL)
        {
!         unsigned long count = 0;
  
!         if (sched_getaffinity_np (getpid (), cpuset_size (set), set) == 0)
            {
!             cpuid_t i;
  
!             for (i = 0;; i++)
                {
!                 int ret = cpuset_isset (i, set);
!                 if (ret < 0)
!                   break;
!                 if (ret > 0)
!                   count++;
                }
            }
+         cpuset_destroy (set);
+         if (count > 0)
+           return count;
        }
+   }
  #endif
  
  #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
!   { /* This works on native Windows platforms.  */
!     DWORD_PTR process_mask;
!     DWORD_PTR system_mask;
  
!     if (GetProcessAffinityMask (GetCurrentProcess (),
!                                 &process_mask, &system_mask))
!       {
!         DWORD_PTR mask = process_mask;
!         unsigned long count = 0;
  
!         for (; mask != 0; mask = mask >> 1)
!           if (mask & 1)
!             count++;
!         if (count > 0)
!           return count;
        }
+   }
  #endif
  
+   return 0;
+ }
+ 
+ unsigned long int
+ num_processors (enum nproc_query query)
+ {
+   if (query == NPROC_CURRENT_OVERRIDABLE)
+     {
+       /* Test the environment variable OMP_NUM_THREADS, recognized also by all
+          programs that are based on OpenMP.  The OpenMP spec says that the
+          value assigned to the environment variable "may have leading and
+          trailing white space". */
+       const char *envvalue = getenv ("OMP_NUM_THREADS");
+ 
+       if (envvalue != NULL)
+         {
+           while (*envvalue != '\0' && c_isspace (*envvalue))
+             envvalue++;
+           /* Convert it from decimal to 'unsigned long'.  */
+           if (c_isdigit (*envvalue))
+             {
+               char *endptr = NULL;
+               unsigned long int value = strtoul (envvalue, &endptr, 10);
+ 
+               if (endptr != NULL)
+                 {
+                   while (*endptr != '\0' && c_isspace (*endptr))
+                     endptr++;
+                   if (*endptr == '\0')
+                     return (value > 0 ? value : 1);
+                 }
+             }
+         }
+ 
+       query = NPROC_CURRENT;
+     }
+   /* Here query is one of NPROC_ALL, NPROC_CURRENT.  */
+ 
+   /* On systems with a modern affinity mask system call, we have
+          sysconf (_SC_NPROCESSORS_CONF)
+             >= sysconf (_SC_NPROCESSORS_ONLN)
+                >= num_processors_via_affinity_mask ()
+      The first number is the number of CPUs configured in the system.
+      The second number is the number of CPUs available to the scheduler.
+      The third number is the number of CPUs available to the current process.
+ 
+      Note! On Linux systems with glibc, the first and second number come from
+      the /sys and /proc file systems (see
+      glibc/sysdeps/unix/sysv/linux/getsysstats.c).
+      In some situations these file systems are not mounted, and the sysconf
+      call returns 1, which does not reflect the reality.  */
+ 
+   if (query == NPROC_CURRENT)
+     {
+       /* Try the modern affinity mask system call.  */
+       {
+         unsigned long nprocs = num_processors_via_affinity_mask ();
+ 
+         if (nprocs > 0)
+           return nprocs;
+       }
+ 
  #if defined _SC_NPROCESSORS_ONLN
        { /* This works on glibc, MacOS X 10.5, FreeBSD, AIX, OSF/1, Solaris,
             Cygwin, Haiku.  */
***************
*** 239,244 ****
--- 271,292 ----
        { /* This works on glibc, MacOS X 10.5, FreeBSD, AIX, OSF/1, Solaris,
             Cygwin, Haiku.  */
          long int nprocs = sysconf (_SC_NPROCESSORS_CONF);
+ 
+ # if __GLIBC__ >= 2 && defined __linux__
+         /* On Linux systems with glibc, this information comes from the /sys 
and
+            /proc file systems (see 
glibc/sysdeps/unix/sysv/linux/getsysstats.c).
+            In some situations these file systems are not mounted, and the
+            sysconf call returns 1.  But we wish to guarantee that
+            num_processors (NPROC_ALL) >= num_processors (NPROC_CURRENT).  */
+         if (nprocs == 1)
+           {
+             unsigned long nprocs_current = num_processors_via_affinity_mask 
();
+ 
+             if (nprocs_current > 0)
+               nprocs = nprocs_current;
+           }
+ # endif
+ 
          if (nprocs > 0)
            return nprocs;
        }




reply via email to

[Prev in Thread] Current Thread [Next in Thread]