Fix initial thread affinity on Linux (#24801)
authorJan Vorlicek <janvorli@microsoft.com>
Tue, 28 May 2019 16:06:38 +0000 (18:06 +0200)
committerGitHub <noreply@github.com>
Tue, 28 May 2019 16:06:38 +0000 (18:06 +0200)
* Fix initial thread affinity on Linux

On Linux, a new thread inherits the affinity mask of the thread
that created the new thread. This is a problem for background GC
threads that are created by one of the server GC threads that are
affinitized to single core.
This change adds resetting each new thread affinity to match the
current process affinity.
In addition to that, I've also fixed the extraction of the CPU count
that was using PID 0. While the doc says that 0 represents current process,
it in fact means current thread.
And as a small bonus, I've added caching of the value returned by
the PAL_GetLogicalCpuCountFromOS, since it cannot change during runtime.

src/gc/unix/gcenv.unix.cpp
src/pal/src/config.h.in
src/pal/src/configure.cmake
src/pal/src/misc/sysinfo.cpp
src/pal/src/thread/thread.cpp

index 2828c94..3d7442a 100644 (file)
@@ -289,7 +289,7 @@ bool GCToOSInterface::Initialize()
     g_currentProcessCpuCount = 0;
 
     cpu_set_t cpuSet;
-    int st = sched_getaffinity(0, sizeof(cpu_set_t), &cpuSet);
+    int st = sched_getaffinity(getpid(), sizeof(cpu_set_t), &cpuSet);
 
     if (st == 0)
     {
index 3ceb180..505b2ee 100644 (file)
@@ -36,6 +36,7 @@
 #cmakedefine01 HAVE_PTHREAD_GETCPUCLOCKID
 #cmakedefine01 HAVE_PTHREAD_SIGQUEUE
 #cmakedefine01 HAVE_PTHREAD_GETAFFINITY_NP
+#cmakedefine01 HAVE_PTHREAD_ATTR_SETAFFINITY_NP
 #cmakedefine01 HAVE_CPUSET_T
 #cmakedefine01 HAVE_SIGRETURN
 #cmakedefine01 HAVE__THREAD_SYS_SIGRETURN
index d214dd3..7b6391f 100644 (file)
@@ -101,6 +101,7 @@ check_library_exists(${PTHREAD_LIBRARY} pthread_getattr_np "" HAVE_PTHREAD_GETAT
 check_library_exists(${PTHREAD_LIBRARY} pthread_getcpuclockid "" HAVE_PTHREAD_GETCPUCLOCKID)
 check_library_exists(${PTHREAD_LIBRARY} pthread_sigqueue "" HAVE_PTHREAD_SIGQUEUE)
 check_library_exists(${PTHREAD_LIBRARY} pthread_getaffinity_np "" HAVE_PTHREAD_GETAFFINITY_NP)
+check_library_exists(${PTHREAD_LIBRARY} pthread_attr_setaffinity_np "" HAVE_PTHREAD_ATTR_SETAFFINITY_NP)
 
 check_function_exists(sigreturn HAVE_SIGRETURN)
 check_function_exists(_thread_sys_sigreturn HAVE__THREAD_SYS_SIGRETURN)
index 1a1a12f..d0e8ca6 100644 (file)
@@ -80,6 +80,7 @@ Revision History:
 #endif
 
 #include "pal/dbgmsg.h"
+#include "pal/process.h"
 
 #include <algorithm>
 
@@ -139,21 +140,24 @@ DWORD
 PALAPI
 PAL_GetLogicalCpuCountFromOS()
 {
-    int nrcpus = 0;
+    static int nrcpus = -1;
 
+    if (nrcpus == -1)
+    {
 #if HAVE_SCHED_GETAFFINITY
 
-    cpu_set_t cpuSet;
-    int st = sched_getaffinity(0, sizeof(cpu_set_t), &cpuSet);
-    if (st != 0)
-    {
-        ASSERT("sched_getaffinity failed (%d)\n", errno);
-    }
+        cpu_set_t cpuSet;
+        int st = sched_getaffinity(gPID, sizeof(cpu_set_t), &cpuSet);
+        if (st != 0)
+        {
+            ASSERT("sched_getaffinity failed (%d)\n", errno);
+        }
 
-    nrcpus = CPU_COUNT(&cpuSet);
+        nrcpus = CPU_COUNT(&cpuSet);
 #else // HAVE_SCHED_GETAFFINITY
-    nrcpus = PAL_GetTotalCpuCount();
+        nrcpus = PAL_GetTotalCpuCount();
 #endif // HAVE_SCHED_GETAFFINITY
+    }
 
     return nrcpus;
 }
index 21775f1..34d7808 100644 (file)
@@ -760,6 +760,42 @@ CorUnix::InternalCreateThread(
 #ifdef FEATURE_PAL_SXS
     _ASSERT_MSG(pNewThread->IsInPal(), "New threads we're about to spawn should always be in the PAL.\n");
 #endif // FEATURE_PAL_SXS
+
+#if HAVE_PTHREAD_ATTR_SETAFFINITY_NP && HAVE_SCHED_GETAFFINITY
+    {
+        // Threads inherit their parent's affinity mask on Linux. This is not desired, so we reset
+        // the current thread's affinity mask to the mask of the current process.
+        cpu_set_t cpuSet;
+        CPU_ZERO(&cpuSet);
+
+        int st = sched_getaffinity(gPID, sizeof(cpu_set_t), &cpuSet);
+        if (st != 0)
+        {
+            ASSERT("sched_getaffinity failed!\n");
+            // the sched_getaffinity should never fail for getting affinity of the current process
+            palError = ERROR_INTERNAL_ERROR;
+            goto EXIT;
+        }
+
+        st = pthread_attr_setaffinity_np(&pthreadAttr, sizeof(cpu_set_t), &cpuSet);
+        if (st != 0)
+        {
+            if (st == ENOMEM)
+            {
+                palError = ERROR_NOT_ENOUGH_MEMORY;
+            }
+            else
+            {
+                ASSERT("pthread_attr_setaffinity_np failed!\n");
+                // The pthread_attr_setaffinity_np should never fail except of OOM when
+                // passed the mask extracted using sched_getaffinity.
+                palError = ERROR_INTERNAL_ERROR;
+            }
+            goto EXIT;
+        }
+    }
+#endif // HAVE_PTHREAD_GETAFFINITY_NP && HAVE_SCHED_GETAFFINITY
+
     iError = pthread_create(&pthread, &pthreadAttr, CPalThread::ThreadEntry, pNewThread);
 
 #if PTHREAD_CREATE_MODIFIES_ERRNO