[m-rev.] for review: Fix use of Linux CPU affinity API.

Peter Wang novalazy at gmail.com
Wed May 16 17:18:20 AEST 2018


For review by Paul or anyone.

----

The main fix is to retry the sched_getaffinity() call in
MR_reset_available_cpus(). If that call failed because we provided a
cpuset that is too small for the kernel, MR_reset_available_cpus() would
set MR_available_cpus to NULL but leave MR_cpuset_size non-zero,
leading to a null pointer dereference soon after.

runtime/mercury_context.c:
runtime/mercury_context.h:
    Use a macro MR_HAVE_LINUX_CPU_AFFINITY_API if we have all the pieces
    we need for that API.

    Rename MR_num_processors to MR_num_processors_detected for clarity.

    Add a global variable MR_cpuset_num_cpus to record the size of the
    cpuset that we allocated.

    Change MR_cpuset_size to size_t to match the type in the underlying
    interface.

    Rename MR_available_cpus to MR_cpuset_available to clarify the
    relationship with MR_cpuset_size and MR_cpuset_num_cpus.

    In MR_reset_available_cpus(), retry sched_getaffinity() if it fails
    with EINVAL. It may fail because the kernel uses a larger CPU
    affinity mask than the cpuset we provided; then we must retry the
    call with a larger cpuset.

    Ensure MR_reset_available_cpus() does not leave MR_cpuset_*
    variables in an inconsistent state.

    Make MR_detect_num_processors() set MR_num_processors to 1 if the
    MR_cpuset_available set is somehow empty after
    MR_reset_available_cpus(). It should not happen, but the program
    should be able to continue anyway.

    In MR_pin_thread_no_locking(), loop over the entire
    MR_cpuset_available set to find a target CPU to pin to.
    The CPUs available to the thread are not necessarily numbered
    consecutively from the initial CPU onwards mod MR_num_processors.

    Fix cpuset allocation in MR_do_pin_thread to not assume that CPUs
    are numbered from 0 to MR_num_processors-1. Fix a memory leak.

    Clean up MR_current_cpu().
    Prevent a null pointer dereference in the hwloc path:
    hwloc_get_pu_obj_by_os_index always returns NULL on my system.

    Change some types from 'unsigned' to 'int' to match underlying APIs.

library/thread.m:
    Conform to variable renaming.
---
 library/thread.m          |   2 +-
 runtime/mercury_context.c | 277 ++++++++++++++++++++++++--------------
 runtime/mercury_context.h |  10 +-
 3 files changed, 180 insertions(+), 109 deletions(-)

diff --git a/library/thread.m b/library/thread.m
index 0c3a0b2c4..0e11177ed 100644
--- a/library/thread.m
+++ b/library/thread.m
@@ -808,21 +808,21 @@ num_processors(MaybeProcs, !IO) :-
     ).
 
 :- pred num_processors(int::out, bool::out, io::di, io::uo) is det.
 
 :- pragma foreign_proc("C",
     num_processors(Procs::out, Success::out, _IO0::di, _IO::uo),
     [promise_pure, thread_safe, will_not_call_mercury,
      will_not_throw_exception, tabled_for_io],
 "
 #ifdef MR_THREAD_SAFE
-    Procs = MR_num_processors;
+    Procs = MR_num_processors_detected;
     Success = MR_YES;
 #else
     Procs = 1;
     Success = MR_YES;
 #endif
 ").
 
 :- pragma foreign_proc("Java",
     num_processors(Procs::out, Success::out, _IO0::di, _IO::uo),
     [promise_pure, thread_safe, will_not_call_mercury,
diff --git a/runtime/mercury_context.c b/runtime/mercury_context.c
index a050c865b..cf573380c 100644
--- a/runtime/mercury_context.c
+++ b/runtime/mercury_context.c
@@ -1,14 +1,14 @@
 // vim: ts=4 sw=4 expandtab ft=c
 
 // Copyright (C) 1995-2007, 2009-2011 The University of Melbourne.
-// Copyright (C) 2014, 2016 The Mercury team.
+// Copyright (C) 2014, 2016-2018 The Mercury team.
 // This file may only be copied under the terms of the GNU Library General
 // Public License - see the file COPYING.LIB in the Mercury distribution.
 
 // mercury_context.c - handles multithreading stuff.
 
 /*
 INIT mercury_sys_init_scheduler_wrapper
 ENDINIT
 */
 
@@ -33,47 +33,47 @@ ENDINIT
   #include <sys/types.h>    // for fd_set
   #include <sys/time.h>     // for struct timeval
   #ifdef MR_HAVE_UNISTD_H
     #include <unistd.h>     // for select() on OS X
   #endif
 #endif
 #ifdef MR_PROFILE_PARALLEL_EXECUTION_SUPPORT
   #include <math.h>         // for sqrt and pow
 #endif
 
-#ifdef MR_HAVE_SCHED_H
-  #include <sched.h>
+#if defined(MR_THREAD_SAFE) && defined(MR_HAVE_HWLOC)
+  #include <hwloc.h>
+#endif
 
-  // These macros first appeared in glibc 2.7.
-  #if defined(CPU_ALLOC) && defined(CPU_ALLOC_SIZE) && defined(CPU_FREE) && \
-      defined(CPU_ZERO_S) && defined(CPU_SET_S) && defined(CPU_CLR_S) && \
-      defined(CPU_ISSET_S) && defined(CPU_COUNT_S)
-    #define MR_HAVE_CPU_SET_MACROS 1
-  #endif
+// The CPU_* macros first appeared in glibc 2.7.
+#if defined(MR_HAVE_SCHED_H) && \
+    defined(MR_HAVE_SCHED_GETAFFINITY) && \
+    defined(MR_HAVE_SCHED_SETAFFINITY) && \
+    defined(CPU_ALLOC) && defined(CPU_ALLOC_SIZE) && defined(CPU_FREE) && \
+    defined(CPU_ZERO_S) && defined(CPU_SET_S) && defined(CPU_CLR_S) && \
+    defined(CPU_ISSET_S) && defined(CPU_COUNT_S)
+  #include <sched.h>
+  #define MR_HAVE_LINUX_CPU_AFFINITY_API 1
 #endif
 
 #ifdef MR_MINGW
   #include <sys/time.h>     // for gettimeofday()
 #endif
 
 #ifdef MR_WIN32
   #include <sys/timeb.h>    // for _ftime()
 #endif
 
 #ifdef MR_WIN32_GETSYSTEMINFO
   #include "mercury_windows.h"
 #endif
 
-#if defined(MR_THREAD_SAFE) && defined(MR_HAVE_HWLOC)
-  #include <hwloc.h>
-#endif
-
 #include "mercury_memory_handlers.h"
 #include "mercury_context.h"
 #include "mercury_engine.h"             // for `MR_memdebug'
 #include "mercury_threadscope.h"        // for data types and posting events
 #include "mercury_reg_workarounds.h"    // for `MR_fd*' stuff
 
 #ifdef MR_PROFILE_PARALLEL_EXECUTION_SUPPORT
 #define MR_PROFILE_PARALLEL_EXECUTION_FILENAME "parallel_execution_profile.txt"
 #endif
 
@@ -201,30 +201,40 @@ static MR_Integer       MR_profile_parallel_contexts_created_for_sparks = 0;
 // list lock.
 
 static MR_Integer       MR_profile_parallel_small_context_reused = 0;
 static MR_Integer       MR_profile_parallel_regular_context_reused = 0;
 static MR_Integer       MR_profile_parallel_small_context_kept = 0;
 static MR_Integer       MR_profile_parallel_regular_context_kept = 0;
   #endif // ! MR_HIGHLEVEL_CODE
 #endif // MR_PROFILE_PARALLEL_EXECUTION_SUPPORT
 
 #ifdef MR_THREAD_SAFE
-// Detect number of processors.
 
-unsigned         MR_num_processors;
+// The detected number of processors available to this process.
+unsigned         MR_num_processors_detected;
+
 #if defined(MR_HAVE_HWLOC)
     static hwloc_topology_t MR_hw_topology;
     static hwloc_cpuset_t   MR_hw_available_pus = NULL;
-#elif defined(MR_HAVE_SCHED_SETAFFINITY)
-    static cpu_set_t        *MR_available_cpus;
-    // The number of CPUs that MR_available_cpus can refer to.
-    static unsigned         MR_cpuset_size = 0;
+#elif defined(MR_HAVE_LINUX_CPU_AFFINITY_API)
+    // The number of CPUs that can be represented by MR_cpuset_available.
+    static int          MR_cpuset_num_cpus = 0;
+
+    // The size of MR_cpuset_available in bytes, given by
+    // CPU_ALLOC_SIZE(MR_cpuset_num_cpus).
+    static size_t       MR_cpuset_size = 0;
+
+    // A cpuset of MR_cpuset_size bytes, able to represent processors in the
+    // range [0, MR_cpuset_num_cpus).
+    // NOTE: the processors available to a process are NOT necessarily
+    // numbered from 0 to MR_num_processors_detected-1.
+    static cpu_set_t    *MR_cpuset_available;
 #endif
 
 // Local variables for thread pinning.
 
 #if defined(MR_LL_PARALLEL_CONJ) && defined(MR_HAVE_THREAD_PINNING)
 MR_bool                 MR_thread_pinning = MR_FALSE;
 static MercuryLock      MR_thread_pinning_lock;
 static unsigned         MR_num_threads_left_to_pin;
 MR_Unsigned             MR_primordial_thread_cpu;
 #endif
@@ -352,21 +362,21 @@ MR_init_context_stuff(void)
     MR_sem_init(&shutdown_ws_semaphore, 0);
   #endif
     pthread_mutex_init(&MR_STM_lock, MR_MUTEX_ATTR);
 
   #ifdef MR_HIGHLEVEL_CODE
     MR_KEY_CREATE(&MR_backjump_handler_key, NULL);
     MR_KEY_CREATE(&MR_backjump_next_choice_id_key, (void *)0);
   #endif
 
     MR_detect_num_processors();
-    assert(MR_num_processors > 0);
+    assert(MR_num_processors_detected > 0);
 
   #ifdef MR_LL_PARALLEL_CONJ
     MR_setup_num_threads();
     assert(MR_num_ws_engines > 0);
 
     #if defined(MR_HAVE_THREAD_PINNING)
     MR_setup_thread_pinning();
     #endif
 
     MR_granularity_wsdeque_length =
@@ -416,154 +426,204 @@ MR_reset_available_cpus(void)
     // libhwloc_1.0-1 (Debian) hwloc reported that all cpus on the system are
     // available, it didn't exclude cpus not in the processor's cpuset(7).
 
     if (MR_hw_available_pus == NULL) {
         MR_hw_available_pus = hwloc_bitmap_alloc();
     }
     hwloc_bitmap_and(MR_hw_available_pus, inherited_binding,
         hwloc_topology_get_allowed_cpuset(MR_hw_topology));
 
     hwloc_bitmap_free(inherited_binding);
-  #elif defined(MR_HAVE_SCHED_GETAFFINITY) && defined(MR_HAVE_CPU_SET_MACROS)
-    unsigned cpuset_size;
-    unsigned num_processors;
-
-    if (MR_cpuset_size) {
-        cpuset_size = MR_cpuset_size;
-        num_processors = MR_num_processors;
+  #elif defined(MR_HAVE_LINUX_CPU_AFFINITY_API)
+    int         num_cpus;
+    size_t      cpuset_size = 0;
+    cpu_set_t   *cpuset = NULL;
+
+    if (MR_cpuset_num_cpus > 0) {
+        // Start with the same cpuset size that we determined in a previous
+        // call to this function.
+        num_cpus = MR_cpuset_num_cpus;
     } else {
       #if defined(MR_HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)
-        num_processors = sysconf(_SC_NPROCESSORS_ONLN);
+        num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
       #else
         // Make the CPU set at least 32 processors wide.
-
-        num_processors = 32;
+        // The minimum cpuset size on 32-bit architectures is 32-bits.
+        num_cpus = 32;
       #endif
-        cpuset_size = CPU_ALLOC_SIZE(num_processors);
-        MR_cpuset_size = cpuset_size;
     }
 
-    if (MR_available_cpus == NULL) {
-        MR_available_cpus = CPU_ALLOC(num_processors);
+    // Free an existing cpuset if we have been here before.
+    if (MR_cpuset_available != NULL) {
+        CPU_FREE(MR_cpuset_available);
+        MR_cpuset_available = NULL;
+        MR_cpuset_size = 0;
+        MR_cpuset_num_cpus = 0;
     }
 
-    if (-1 == sched_getaffinity(0, cpuset_size, MR_available_cpus)) {
+    // This huge limit is just to prevent the possibility of looping forever.
+    // In most cases we will succeed on the first attempt.
+    while (num_cpus <= 0x100000) {
+        int err;
+
+        cpuset_size = CPU_ALLOC_SIZE(num_cpus);
+        cpuset = CPU_ALLOC(num_cpus);
+        if (cpuset == NULL) {
+            break;
+        }
+        if (sched_getaffinity(0, cpuset_size, cpuset) == 0) {
+            break;
+        }
+        err = errno;
+        CPU_FREE(cpuset);
+        cpuset = NULL;
+        if (err != EINVAL) {
+            break;
+        }
+        // sched_getaffinity() can return EINVAL if the kernel uses larger CPU
+        // affinity masks than the one we provided. Then we must retry with a
+        // larger mask size.
+        if (num_cpus < 512) {
+            num_cpus = 512;
+        } else {
+            num_cpus *= 2;
+        }
+    }
+
+    if (cpuset != NULL) {
+        MR_cpuset_num_cpus = num_cpus;
+        MR_cpuset_size = cpuset_size;
+        MR_cpuset_available = cpuset;
+    } else {
         MR_perror("Couldn't get CPU affinity");
       #if defined(MR_LL_PARALLEL_CONJ) && defined(MR_HAVE_THREAD_PINNING)
         MR_thread_pinning = MR_FALSE;
       #endif
-        CPU_FREE(MR_available_cpus);
-        MR_available_cpus = NULL;
     }
   #endif
 }
 
 static void
 MR_detect_num_processors(void)
 {
   #ifdef MR_HAVE_HWLOC
     if (-1 == hwloc_topology_init(&MR_hw_topology)) {
         MR_fatal_error("Error allocating libhwloc topology object");
     }
     if (-1 == hwloc_topology_load(MR_hw_topology)) {
         MR_fatal_error("Error detecting hardware topology (hwloc)");
     }
   #endif
 
     // Setup num processors.
 
     MR_reset_available_cpus();
-  #ifdef MR_HAVE_HWLOC
-    MR_num_processors = hwloc_bitmap_weight(MR_hw_available_pus);
-  #elif defined(MR_HAVE_SCHED_GETAFFINITY) && defined(MR_HAVE_CPU_SET_MACROS)
-    MR_num_processors = CPU_COUNT_S(MR_cpuset_size, MR_available_cpus);
+  #if defined(MR_HAVE_HWLOC)
+    MR_num_processors_detected = hwloc_bitmap_weight(MR_hw_available_pus);
+  #elif defined(MR_HAVE_LINUX_CPU_AFFINITY_API)
+    MR_num_processors_detected =
+        CPU_COUNT_S(MR_cpuset_size, MR_cpuset_available);
+    if (MR_num_processors_detected == 0) {
+        // Carry on even if the MR_cpuset_available is somehow empty.
+        MR_num_processors_detected = 1;
+    }
   #elif defined(MR_WIN32_GETSYSTEMINFO)
     {
         SYSTEM_INFO sysinfo;
         GetSystemInfo(&sysinfo);
-        MR_num_processors = sysinfo.dwNumberOfProcessors;
+        MR_num_processors_detected = sysinfo.dwNumberOfProcessors;
     }
   #elif defined(MR_HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)
-    MR_num_processors = sysconf(_SC_NPROCESSORS_ONLN);
+    MR_num_processors_detected = sysconf(_SC_NPROCESSORS_ONLN);
   #else
-    #warning "Cannot detect MR_num_processors"
-    MR_num_processors = 1;
+    #warning "Cannot detect MR_num_processors_detected"
+    MR_num_processors_detected = 1;
   #endif
 }
 
 #ifdef MR_LL_PARALLEL_CONJ
 static void
 MR_setup_num_threads(void)
 {
     // If MR_num_threads is unset, configure it to match number of processors
     // on the system. If we do this, then we prepare to set processor
     // affinities later on.
 
     if (MR_num_ws_engines == 0) {
-        MR_num_ws_engines = MR_num_processors;
+        MR_num_ws_engines = MR_num_processors_detected;
     }
 
   #ifdef MR_DEBUG_THREADS
     if (MR_debug_threads) {
         fprintf(stderr, "Detected %d processors, will use %d threads\n",
-            MR_num_processors, MR_num_ws_engines);
+            MR_num_processors_detected, MR_num_ws_engines);
     }
   #endif
 }
 #endif // MR_LL_PARALLEL_CONJ
 #endif // MR_THREAD_SAFE
 
 // Thread pinning.
 
 #if defined(MR_HAVE_THREAD_PINNING) && defined(MR_LL_PARALLEL_CONJ)
-// Pin the primordial thread first to the CPU it is currently using
-// (if support is available for thread pinning).
 
-static unsigned
+static int
 MR_pin_thread_no_locking(void)
 {
-    unsigned    cpu;
-    unsigned    i = 0;
+    int     initial_cpu;
+    int     max;
+    int     i;
 
-    cpu = MR_current_cpu();
-#ifdef MR_DEBUG_THREAD_PINNING
-    fprintf(stderr, "Currently running on cpu %d\n", cpu);
-#endif
+    initial_cpu = MR_current_cpu();
+  #ifdef MR_DEBUG_THREAD_PINNING
+    fprintf(stderr, "Currently running on cpu %d\n", initial_cpu);
+  #endif
 
-    for (i = 0; (i < MR_num_processors) && MR_thread_pinning; i++) {
-        if (MR_do_pin_thread((cpu + i) % MR_num_processors)) {
-#ifdef MR_DEBUG_THREAD_PINNING
-            fprintf(stderr, "Pinned to cpu %d\n", (cpu + i) % MR_num_processors);
-            fprintf(stderr, "Now running on cpu %d\n", MR_current_cpu());
-#endif
+  #if defined(MR_HAVE_HWLOC)
+    max = MR_num_processors_detected;
+  #elif defined(MR_HAVE_LINUX_CPU_AFFINITY_API)
+    // CPUs available to this process do not have to be numbered consecutively.
+    max = MR_cpuset_num_cpus;
+  #else
+    #error Should be unreachable
+  #endif
+
+    for (i = 0; (i < max) && MR_thread_pinning; i++) {
+        int target_cpu = (initial_cpu + i) % max;
+
+        if (MR_do_pin_thread(target_cpu)) {
+          #ifdef MR_DEBUG_THREAD_PINNING
+            fprintf(stderr, "Pinned to cpu %d, running on cpu %d\n",
+                target_cpu, MR_current_cpu());
+          #endif
             MR_num_threads_left_to_pin--;
-            MR_make_cpu_unavailable((cpu + i) % MR_num_processors);
-            return (cpu + i) % MR_num_processors;
+            MR_make_cpu_unavailable(target_cpu);
+            return target_cpu;
         }
+
         if (!MR_thread_pinning) {
             // If MR_thread_pinning becomes false then an error prevented us
             // from pinning the thread.
             // When we fail to pin a thread but MR_thread_pinning remains true
             // it means that CPU has already had a thread pinned to it.
-
             fprintf(stderr, "Couldn't pin Mercury engine to processor");
             break;
         }
     }
 
-    return cpu;
+    return initial_cpu;
 }
 
-unsigned
+int
 MR_pin_thread(void)
 {
-    unsigned cpu;
+    int cpu;
 
     MR_LOCK(&MR_thread_pinning_lock, "MR_pin_thread");
     cpu = MR_pin_thread_no_locking();
     MR_UNLOCK(&MR_thread_pinning_lock, "MR_pin_thread");
 
     return cpu;
 }
 
 void
 MR_pin_primordial_thread(void)
@@ -577,57 +637,56 @@ MR_pin_primordial_thread(void)
 static void MR_setup_thread_pinning(void)
 {
     MR_num_threads_left_to_pin = MR_num_ws_engines;
 
     pthread_mutex_init(&MR_thread_pinning_lock, MR_MUTEX_ATTR);
 
   // Comment this back in to enable thread pinning by default
   // if we autodetected the number of CPUs without error.
 
 #if 0
-    if (MR_num_processors > 1) {
+    if (MR_num_processors_detected > 1) {
         MR_thread_pinning = MR_TRUE;
     }
 #endif
 }
 
 // Determine which CPU this thread is currently running on.
 
 static int MR_current_cpu(void)
 {
-#if defined(MR_HAVE_SCHED_GETCPU)
-    int         os_cpu;
+    int os_cpu;
+
 #if defined(MR_HAVE_HWLOC)
     hwloc_obj_t pu;
+    pu = hwloc_get_pu_obj_by_os_index(MR_hw_topology, os_cpu);
+    if (pu != NULL) {
+        os_cpu = pu->logical_index;
+    } else {
+        // XXX Quick hack to prevent crashes only.
+        os_cpu = -1;
+    }
+#elif defined(MR_HAVE_SCHED_GETCPU)
+    os_cpu = sched_getcpu();
+#else
+    os_cpu = 0;
 #endif
 
-    os_cpu = sched_getcpu();
-    if (-1 == os_cpu) {
+    if (os_cpu < 0) {
         os_cpu = 0;
-
         if (MR_thread_pinning) {
             MR_perror("Warning: unable to determine the current CPU for "
                 "this thread: ");
         }
     }
 
-#if defined(MR_HAVE_HWLOC)
-    pu = hwloc_get_pu_obj_by_os_index(MR_hw_topology, os_cpu);
-    return pu->logical_index;
-#else
     return os_cpu;
-#endif
-
-#else // ! MR_HAVE_SCHED_GETCPU
-    // We have no idea!
-    return 0;
-#endif
 }
 
 static MR_bool
 MR_do_pin_thread(int cpu)
 {
     // Make sure that we are allowed to bind to this CPU.
 
 #if defined(MR_HAVE_HWLOC)
     hwloc_obj_t pu;
 
@@ -636,70 +695,84 @@ MR_do_pin_thread(int cpu)
         // available_pus set so that we can oversubscribe CPUs but still
         // attempt to balance load.
 
         MR_reset_available_cpus();
     }
 
     pu = hwloc_get_obj_by_type(MR_hw_topology, HWLOC_OBJ_PU, cpu);
     if (!hwloc_bitmap_intersects(MR_hw_available_pus, pu->cpuset)) {
         return MR_FALSE;
     }
-#elif defined(MR_HAVE_SCHED_SETAFFINITY) && defined(MR_HAVE_CPU_SET_MACROS)
-    if (CPU_COUNT_S(MR_cpuset_size, MR_available_cpus) == 0) {
+#elif defined(MR_HAVE_LINUX_CPU_AFFINITY_API)
+    if (CPU_COUNT_S(MR_cpuset_size, MR_cpuset_available) == 0) {
         // As above, reset the available cpus.
 
         MR_reset_available_cpus();
     }
-    if (!CPU_ISSET_S(cpu, MR_cpuset_size, MR_available_cpus)) {
+    if (!CPU_ISSET_S(cpu, MR_cpuset_size, MR_cpuset_available)) {
         return MR_FALSE;
     }
 #endif
 
 #if defined(MR_HAVE_HWLOC)
     errno = hwloc_set_cpubind(MR_hw_topology, pu->cpuset,
         HWLOC_CPUBIND_THREAD);
     if (errno != 0) {
         MR_perror("Warning: Couldn't set CPU affinity: ");
         MR_thread_pinning = MR_FALSE;
         return MR_FALSE;
     }
-#elif defined(MR_HAVE_SCHED_SETAFFINITY) && defined(MR_HAVE_CPU_SET_MACROS)
-    cpu_set_t   *cpus;
-
-    cpus = CPU_ALLOC(MR_num_processors);
-
-    CPU_ZERO_S(MR_cpuset_size, cpus);
-    CPU_SET_S(cpu, MR_cpuset_size, cpus);
-    if (sched_setaffinity(0, MR_cpuset_size, cpus) == -1) {
-        MR_perror("Warning: Couldn't set CPU affinity: ");
-        // If this failed once, it will probably fail again, so we disable it.
-
-        MR_thread_pinning = MR_FALSE;
-        return MR_FALSE;
+#elif defined(MR_HAVE_LINUX_CPU_AFFINITY_API)
+    size_t      cpuset_size;
+    cpu_set_t   *cpuset;
+    MR_bool     success;
+
+    // The man page for sched_setaffinity() says that Linux 2.6.9 and earlier
+    // may return EINVAL if given a cpuset size smaller than size of the
+    // affinity mask used by the kernel, so we allocate a cpuset large enough
+    // for MR_cpuset_num_cpus, not just cpu.
+    cpuset_size = CPU_ALLOC_SIZE(MR_cpuset_num_cpus);
+    cpuset = CPU_ALLOC(MR_cpuset_num_cpus);
+    if (cpuset != NULL) {
+        CPU_ZERO_S(cpuset_size, cpuset);
+        CPU_SET_S(cpu, cpuset_size, cpuset);
+        if (sched_setaffinity(0, cpuset_size, cpuset) == 0) {
+            success = MR_TRUE;
+        } else {
+            MR_perror("Warning: Couldn't set CPU affinity: ");
+            // If this failed once, it will probably fail again.
+            // Disable thread pinning from now on.
+            MR_thread_pinning = MR_FALSE;
+            success = MR_FALSE;
+        }
+        CPU_FREE(cpuset);
+    } else {
+        success = MR_FALSE;
     }
+    return success;
 #endif
 
     return MR_TRUE;
 }
 
 #if defined(MR_HAVE_HWLOC)
 static MR_bool  MR_make_pu_unavailable(const struct hwloc_obj *pu);
 #endif
 
 static void MR_make_cpu_unavailable(int cpu)
 {
 #if defined(MR_HAVE_HWLOC)
     hwloc_obj_t pu;
     pu = hwloc_get_obj_by_type(MR_hw_topology, HWLOC_OBJ_PU, cpu);
     MR_make_pu_unavailable(pu);
-#elif defined(MR_HAVE_SCHED_SETAFFINITY) && defined(MR_HAVE_CPU_SET_MACROS)
-    CPU_CLR_S(cpu, MR_cpuset_size, MR_available_cpus);
+#elif defined(MR_HAVE_LINUX_CPU_AFFINITY_API)
+    CPU_CLR_S(cpu, MR_cpuset_size, MR_cpuset_available);
 #endif
 }
 
 #if defined(MR_HAVE_HWLOC)
 static MR_bool MR_make_pu_unavailable(const struct hwloc_obj *pu)
 {
     hwloc_obj_t core;
     static int  siblings_to_make_unavailable;
     int         i;
 
diff --git a/runtime/mercury_context.h b/runtime/mercury_context.h
index e15682a69..11d7d880c 100644
--- a/runtime/mercury_context.h
+++ b/runtime/mercury_context.h
@@ -369,22 +369,22 @@ extern      MR_Context  *MR_runqueue_tail;
 #endif
 
 #ifdef MR_PROFILE_PARALLEL_EXECUTION_SUPPORT
 extern MR_bool      MR_profile_parallel_execution;
 
 // XXX: This is currently unused, we plan to use it in the future. -pbone
 extern MR_Stats     MR_profile_parallel_executed_local_sparks;
 #endif
 
 #ifdef MR_THREAD_SAFE
-// The number of processors available.
-extern unsigned     MR_num_processors;
+// The number of processors detected.
+extern unsigned     MR_num_processors_detected;
 #endif
 
 // As well as the runqueue, we maintain a linked list of contexts
 // and associated file descriptors that are suspended blocked for
 // reads/writes/exceptions. When the runqueue becomes empty, if
 // this list is not empty then we call select and block until one
 // or more of the file descriptors become ready for I/O, then
 // wake the appropriate context.
 // In addition, we should periodically check to see if the list of blocked
 // contexts is non-empty and if so, poll to wake any contexts that
@@ -465,24 +465,22 @@ extern  void        MR_init_context_stuff(void);
 // MR_pin_primordial_thread() is a special case for the primordial thread.
 // It should only be executed once, and only by the primordial thread _before_
 // the other threads are started.
 //
 // Both functions return the CPU number that the thread is pinned to or would
 // be pinned to if pinning was both enabled and supported. That is a valid
 // value is always returned even if the thread is not actually pinned.
 
 #if defined(MR_LL_PARALLEL_CONJ)
 #if defined(MR_HAVE_THREAD_PINNING)
-extern void
-MR_pin_primordial_thread(void);
-extern unsigned
-MR_pin_thread(void);
+extern void         MR_pin_primordial_thread(void);
+extern int          MR_pin_thread(void);
 
 // The CPU that the primordial thread is running on.
 
 extern MR_Unsigned        MR_primordial_thread_cpu;
 #endif
 
 // Shutdown all the work-stealing engines.
 // (Exclusive engines shut down by themselves.)
 
 extern void
-- 
2.17.0



More information about the reviews mailing list