From: Jan Vorlicek Date: Tue, 28 May 2019 16:06:38 +0000 (+0200) Subject: Fix initial thread affinity on Linux (#24801) X-Git-Tag: accepted/tizen/unified/20190813.215958~42^2~62 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=3489e56c875c6144c729b51063300c7d60b1ae31;p=platform%2Fupstream%2Fcoreclr.git Fix initial thread affinity on Linux (#24801) * 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. --- diff --git a/src/gc/unix/gcenv.unix.cpp b/src/gc/unix/gcenv.unix.cpp index 2828c94..3d7442a 100644 --- a/src/gc/unix/gcenv.unix.cpp +++ b/src/gc/unix/gcenv.unix.cpp @@ -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) { diff --git a/src/pal/src/config.h.in b/src/pal/src/config.h.in index 3ceb180..505b2ee 100644 --- a/src/pal/src/config.h.in +++ b/src/pal/src/config.h.in @@ -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 diff --git a/src/pal/src/configure.cmake b/src/pal/src/configure.cmake index d214dd3..7b6391f 100644 --- a/src/pal/src/configure.cmake +++ b/src/pal/src/configure.cmake @@ -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) diff --git a/src/pal/src/misc/sysinfo.cpp b/src/pal/src/misc/sysinfo.cpp index 1a1a12f..d0e8ca6 100644 --- a/src/pal/src/misc/sysinfo.cpp +++ b/src/pal/src/misc/sysinfo.cpp @@ -80,6 +80,7 @@ Revision History: #endif #include "pal/dbgmsg.h" +#include "pal/process.h" #include @@ -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; } diff --git a/src/pal/src/thread/thread.cpp b/src/pal/src/thread/thread.cpp index 21775f1..34d7808 100644 --- a/src/pal/src/thread/thread.cpp +++ b/src/pal/src/thread/thread.cpp @@ -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