Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 4075641

Browse files
authored
[3.1] Change new thread's affinity after thread starts, from the same thread, as a workaround for Snap (#28071)
- Snap's default strict confinement doesn't allow setting the affinity of a different thread, and currently doesn't allow `sched_setaffinity(<nonzeroPid>, ...)`, which pthread implementation calls - Switched to use sched_setaffinity(0, ...) where appropriate Fixes dotnet/runtime#1634
1 parent a6c64da commit 4075641

File tree

6 files changed

+79
-46
lines changed

6 files changed

+79
-46
lines changed

src/gc/unix/config.h.in

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
#cmakedefine01 HAVE_PTHREAD_CONDATTR_SETCLOCK
1818
#cmakedefine01 HAVE_MACH_ABSOLUTE_TIME
1919
#cmakedefine01 HAVE_SCHED_GETAFFINITY
20-
#cmakedefine01 HAVE_PTHREAD_GETAFFINITY_NP
20+
#cmakedefine01 HAVE_SCHED_SETAFFINITY
21+
#cmakedefine01 HAVE_PTHREAD_SETAFFINITY_NP
2122
#cmakedefine01 HAVE_PTHREAD_NP_H
2223
#cmakedefine01 HAVE_CPUSET_T
2324
#cmakedefine01 HAVE__SC_AVPHYS_PAGES

src/gc/unix/configure.cmake

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ check_cxx_source_runs("
8585

8686

8787
check_library_exists(c sched_getaffinity "" HAVE_SCHED_GETAFFINITY)
88+
check_library_exists(c sched_setaffinity "" HAVE_SCHED_SETAFFINITY)
8889
check_library_exists(pthread pthread_create "" HAVE_LIBPTHREAD)
8990

9091
if (HAVE_LIBPTHREAD)
@@ -93,7 +94,7 @@ elseif (HAVE_PTHREAD_IN_LIBC)
9394
set(PTHREAD_LIBRARY c)
9495
endif()
9596

96-
check_library_exists(${PTHREAD_LIBRARY} pthread_getaffinity_np "" HAVE_PTHREAD_GETAFFINITY_NP)
97+
check_library_exists(${PTHREAD_LIBRARY} pthread_setaffinity_np "" HAVE_PTHREAD_SETAFFINITY_NP)
9798

9899
check_cxx_symbol_exists(_SC_PHYS_PAGES unistd.h HAVE__SC_PHYS_PAGES)
99100
check_cxx_symbol_exists(_SC_AVPHYS_PAGES unistd.h HAVE__SC_AVPHYS_PAGES)

src/gc/unix/gcenv.unix.cpp

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -782,19 +782,32 @@ size_t GCToOSInterface::GetCacheSizePerLogicalCpu(bool trueSize)
782782
// true if setting the affinity was successful, false otherwise.
783783
bool GCToOSInterface::SetThreadAffinity(uint16_t procNo)
784784
{
785-
#if HAVE_PTHREAD_GETAFFINITY_NP
785+
#if HAVE_SCHED_SETAFFINITY || HAVE_PTHREAD_SETAFFINITY_NP
786786
cpu_set_t cpuSet;
787787
CPU_ZERO(&cpuSet);
788788
CPU_SET((int)procNo, &cpuSet);
789789

790+
// Snap's default strict confinement does not allow sched_setaffinity(<nonzeroPid>, ...) without manually connecting the
791+
// process-control plug. sched_setaffinity(<currentThreadPid>, ...) is also currently not allowed, only
792+
// sched_setaffinity(0, ...). pthread_setaffinity_np(pthread_self(), ...) seems to call
793+
// sched_setaffinity(<currentThreadPid>, ...) in at least one implementation, and does not work. To work around those
794+
// issues, use sched_setaffinity(0, ...) if available and only otherwise fall back to pthread_setaffinity_np(). See the
795+
// following for more information:
796+
// - https://github.com/dotnet/runtime/pull/38795
797+
// - https://github.com/dotnet/runtime/issues/1634
798+
// - https://forum.snapcraft.io/t/requesting-autoconnect-for-interfaces-in-pigmeat-process-control-home/17987/13
799+
#if HAVE_SCHED_SETAFFINITY
800+
int st = sched_setaffinity(0, sizeof(cpu_set_t), &cpuSet);
801+
#else
790802
int st = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuSet);
803+
#endif
791804

792805
return (st == 0);
793806

794-
#else // HAVE_PTHREAD_GETAFFINITY_NP
807+
#else // !(HAVE_SCHED_SETAFFINITY || HAVE_PTHREAD_SETAFFINITY_NP)
795808
// There is no API to manage thread affinity, so let's ignore the request
796809
return false;
797-
#endif // HAVE_PTHREAD_GETAFFINITY_NP
810+
#endif // HAVE_SCHED_SETAFFINITY || HAVE_PTHREAD_SETAFFINITY_NP
798811
}
799812

800813
// Boosts the calling thread's thread priority to a level higher than the default

src/pal/src/config.h.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
#cmakedefine01 HAVE_PTHREAD_GETCPUCLOCKID
3737
#cmakedefine01 HAVE_PTHREAD_SIGQUEUE
3838
#cmakedefine01 HAVE_PTHREAD_GETAFFINITY_NP
39-
#cmakedefine01 HAVE_PTHREAD_ATTR_SETAFFINITY_NP
4039
#cmakedefine01 HAVE_CPUSET_T
4140
#cmakedefine01 HAVE_SIGRETURN
4241
#cmakedefine01 HAVE__THREAD_SYS_SIGRETURN
@@ -65,6 +64,7 @@
6564
#cmakedefine01 HAVE_TTRACE
6665
#cmakedefine01 HAVE_PIPE2
6766
#cmakedefine01 HAVE_SCHED_GETAFFINITY
67+
#cmakedefine01 HAVE_SCHED_SETAFFINITY
6868
#cmakedefine HAVE_UNW_GET_SAVE_LOC
6969
#cmakedefine HAVE_UNW_GET_ACCESSORS
7070
#cmakedefine01 HAVE_XSWDEV

src/pal/src/configure.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ check_include_files(gnu/lib-names.h HAVE_GNU_LIBNAMES_H)
8383
check_function_exists(kqueue HAVE_KQUEUE)
8484

8585
check_library_exists(c sched_getaffinity "" HAVE_SCHED_GETAFFINITY)
86+
check_library_exists(c sched_setaffinity "" HAVE_SCHED_SETAFFINITY)
8687
check_library_exists(pthread pthread_create "" HAVE_LIBPTHREAD)
8788
check_library_exists(c pthread_create "" HAVE_PTHREAD_IN_LIBC)
8889

@@ -102,7 +103,6 @@ check_library_exists(${PTHREAD_LIBRARY} pthread_getattr_np "" HAVE_PTHREAD_GETAT
102103
check_library_exists(${PTHREAD_LIBRARY} pthread_getcpuclockid "" HAVE_PTHREAD_GETCPUCLOCKID)
103104
check_library_exists(${PTHREAD_LIBRARY} pthread_sigqueue "" HAVE_PTHREAD_SIGQUEUE)
104105
check_library_exists(${PTHREAD_LIBRARY} pthread_getaffinity_np "" HAVE_PTHREAD_GETAFFINITY_NP)
105-
check_library_exists(${PTHREAD_LIBRARY} pthread_attr_setaffinity_np "" HAVE_PTHREAD_ATTR_SETAFFINITY_NP)
106106

107107
check_function_exists(sigreturn HAVE_SIGRETURN)
108108
check_function_exists(_thread_sys_sigreturn HAVE__THREAD_SYS_SIGRETURN)

src/pal/src/thread/thread.cpp

Lines changed: 57 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -819,41 +819,6 @@ CorUnix::InternalCreateThread(
819819
_ASSERT_MSG(pNewThread->IsInPal(), "New threads we're about to spawn should always be in the PAL.\n");
820820
#endif // FEATURE_PAL_SXS
821821

822-
#if HAVE_PTHREAD_ATTR_SETAFFINITY_NP && HAVE_SCHED_GETAFFINITY
823-
{
824-
// Threads inherit their parent's affinity mask on Linux. This is not desired, so we reset
825-
// the current thread's affinity mask to the mask of the current process.
826-
cpu_set_t cpuSet;
827-
CPU_ZERO(&cpuSet);
828-
829-
int st = sched_getaffinity(gPID, sizeof(cpu_set_t), &cpuSet);
830-
if (st != 0)
831-
{
832-
ASSERT("sched_getaffinity failed!\n");
833-
// the sched_getaffinity should never fail for getting affinity of the current process
834-
palError = ERROR_INTERNAL_ERROR;
835-
goto EXIT;
836-
}
837-
838-
st = pthread_attr_setaffinity_np(&pthreadAttr, sizeof(cpu_set_t), &cpuSet);
839-
if (st != 0)
840-
{
841-
if (st == ENOMEM)
842-
{
843-
palError = ERROR_NOT_ENOUGH_MEMORY;
844-
}
845-
else
846-
{
847-
ASSERT("pthread_attr_setaffinity_np failed!\n");
848-
// The pthread_attr_setaffinity_np should never fail except of OOM when
849-
// passed the mask extracted using sched_getaffinity.
850-
palError = ERROR_INTERNAL_ERROR;
851-
}
852-
goto EXIT;
853-
}
854-
}
855-
#endif // HAVE_PTHREAD_GETAFFINITY_NP && HAVE_SCHED_GETAFFINITY
856-
857822
iError = pthread_create(&pthread, &pthreadAttr, CPalThread::ThreadEntry, pNewThread);
858823

859824
#if PTHREAD_CREATE_MODIFIES_ERRNO
@@ -1713,6 +1678,10 @@ CPalThread::ThreadEntry(
17131678
PTHREAD_START_ROUTINE pfnStartRoutine;
17141679
LPVOID pvPar;
17151680
DWORD retValue;
1681+
#if HAVE_SCHED_GETAFFINITY && HAVE_SCHED_SETAFFINITY
1682+
cpu_set_t cpuSet;
1683+
int st;
1684+
#endif
17161685

17171686
pThread = reinterpret_cast<CPalThread*>(pvParam);
17181687

@@ -1722,6 +1691,42 @@ CPalThread::ThreadEntry(
17221691
goto fail;
17231692
}
17241693

1694+
#if HAVE_SCHED_GETAFFINITY && HAVE_SCHED_SETAFFINITY
1695+
// Threads inherit their parent's affinity mask on Linux. This is not desired, so we reset
1696+
// the current thread's affinity mask to the mask of the current process.
1697+
//
1698+
// Typically, we would use pthread_attr_setaffinity_np() and have pthread_create() create the thread with the specified
1699+
// affinity. At least one implementation of pthread_create() following a pthread_attr_setaffinity_np() calls
1700+
// sched_setaffinity(<newThreadPid>, ...), which is not allowed under Snap's default strict confinement without manually
1701+
// connecting the process-control plug. To work around that, have the thread set the affinity after it starts.
1702+
// sched_setaffinity(<currentThreadPid>, ...) is also currently not allowed, only sched_setaffinity(0, ...).
1703+
// pthread_setaffinity_np(pthread_self(), ...) seems to call sched_setaffinity(<currentThreadPid>, ...) in at least one
1704+
// implementation, and does not work. Use sched_setaffinity(0, ...) instead. See the following for more information:
1705+
// - https://github.com/dotnet/runtime/pull/38795
1706+
// - https://github.com/dotnet/runtime/issues/1634
1707+
// - https://forum.snapcraft.io/t/requesting-autoconnect-for-interfaces-in-pigmeat-process-control-home/17987/13
1708+
1709+
CPU_ZERO(&cpuSet);
1710+
1711+
st = sched_getaffinity(gPID, sizeof(cpu_set_t), &cpuSet);
1712+
if (st != 0)
1713+
{
1714+
ASSERT("sched_getaffinity failed!\n");
1715+
// The sched_getaffinity should never fail for getting affinity of the current process
1716+
palError = ERROR_INTERNAL_ERROR;
1717+
goto fail;
1718+
}
1719+
1720+
st = sched_setaffinity(0, sizeof(cpu_set_t), &cpuSet);
1721+
if (st != 0)
1722+
{
1723+
ASSERT("sched_setaffinity failed!\n");
1724+
// The sched_setaffinity should never fail when passed the mask extracted using sched_getaffinity
1725+
palError = ERROR_INTERNAL_ERROR;
1726+
goto fail;
1727+
}
1728+
#endif // HAVE_SCHED_GETAFFINITY && HAVE_SCHED_SETAFFINITY
1729+
17251730
#if !HAVE_MACH_EXCEPTIONS
17261731
if (!pThread->EnsureSignalAlternateStack())
17271732
{
@@ -3038,18 +3043,31 @@ BOOL
30383043
PALAPI
30393044
PAL_SetCurrentThreadAffinity(WORD procNo)
30403045
{
3041-
#if HAVE_PTHREAD_GETAFFINITY_NP
3046+
#if HAVE_SCHED_SETAFFINITY || HAVE_PTHREAD_SETAFFINITY_NP
30423047
cpu_set_t cpuSet;
30433048
CPU_ZERO(&cpuSet);
3044-
30453049
CPU_SET(procNo, &cpuSet);
3050+
3051+
// Snap's default strict confinement does not allow sched_setaffinity(<nonzeroPid>, ...) without manually connecting the
3052+
// process-control plug. sched_setaffinity(<currentThreadPid>, ...) is also currently not allowed, only
3053+
// sched_setaffinity(0, ...). pthread_setaffinity_np(pthread_self(), ...) seems to call
3054+
// sched_setaffinity(<currentThreadPid>, ...) in at least one implementation, and does not work. To work around those
3055+
// issues, use sched_setaffinity(0, ...) if available and only otherwise fall back to pthread_setaffinity_np(). See the
3056+
// following for more information:
3057+
// - https://github.com/dotnet/runtime/pull/38795
3058+
// - https://github.com/dotnet/runtime/issues/1634
3059+
// - https://forum.snapcraft.io/t/requesting-autoconnect-for-interfaces-in-pigmeat-process-control-home/17987/13
3060+
#if HAVE_SCHED_SETAFFINITY
3061+
int st = sched_setaffinity(0, sizeof(cpu_set_t), &cpuSet);
3062+
#else
30463063
int st = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuSet);
3064+
#endif
30473065

30483066
return st == 0;
3049-
#else // HAVE_PTHREAD_GETAFFINITY_NP
3067+
#else // !(HAVE_SCHED_SETAFFINITY || HAVE_PTHREAD_SETAFFINITY_NP)
30503068
// There is no API to manage thread affinity, so let's ignore the request
30513069
return FALSE;
3052-
#endif // HAVE_PTHREAD_GETAFFINITY_NP
3070+
#endif // HAVE_SCHED_SETAFFINITY || HAVE_PTHREAD_SETAFFINITY_NP
30533071
}
30543072

30553073
/*++

0 commit comments

Comments
 (0)