diff --git a/src/coreclr/gc/unix/gcenv.unix.cpp b/src/coreclr/gc/unix/gcenv.unix.cpp index 98b0588e2083ae..d087ee6c5074b2 100644 --- a/src/coreclr/gc/unix/gcenv.unix.cpp +++ b/src/coreclr/gc/unix/gcenv.unix.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #if HAVE_SWAPCTL #include @@ -146,7 +147,7 @@ bool GCToOSInterface::Initialize() return false; } - int configuredCpuCount = sysconf(_SC_NPROCESSORS_CONF); + int configuredCpuCount = minipal_get_cpu_max_possible_count(); if (configuredCpuCount == -1) { return false; diff --git a/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp b/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp index dec55db82905fb..6d7ae3bc96f7da 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp @@ -28,6 +28,7 @@ #include "RhConfig.h" #include +#include #include #include #include @@ -456,10 +457,10 @@ void InitializeCurrentProcessCpuCount() { #if HAVE_SCHED_GETAFFINITY - int configuredCpuCount = sysconf(_SC_NPROCESSORS_CONF); + int configuredCpuCount = minipal_get_cpu_max_possible_count(); if (configuredCpuCount == -1) { - // In the unlikely event that sysconf(_SC_NPROCESSORS_CONF) fails, just assume a reasonable default maximum number of CPUs to avoid failing. + // In the unlikely event that minipal_get_cpu_max_possible_count() fails, just assume a reasonable default maximum number of CPUs to avoid failing. configuredCpuCount = CPU_SETSIZE; } diff --git a/src/coreclr/pal/src/misc/sysinfo.cpp b/src/coreclr/pal/src/misc/sysinfo.cpp index 63b02d00be88d1..f928f839f85c2f 100644 --- a/src/coreclr/pal/src/misc/sysinfo.cpp +++ b/src/coreclr/pal/src/misc/sysinfo.cpp @@ -26,6 +26,7 @@ Revision History: #include #include #include +#include #define __STDC_FORMAT_MACROS #include #include @@ -157,10 +158,10 @@ PAL_GetLogicalCpuCountFromOS() { #if HAVE_SCHED_GETAFFINITY - int configuredCpuCount = sysconf(_SC_NPROCESSORS_CONF); + int configuredCpuCount = minipal_get_cpu_max_possible_count(); if (configuredCpuCount == -1) { - // In the unlikely event that sysconf(_SC_NPROCESSORS_CONF) fails, just assume a reasonable default maximum number of CPUs to avoid failing. + // In the unlikely event that minipal_get_cpu_max_possible_count() fails, just assume a reasonable default maximum number of CPUs to avoid failing. configuredCpuCount = CPU_SETSIZE; } diff --git a/src/coreclr/pal/src/thread/thread.cpp b/src/coreclr/pal/src/thread/thread.cpp index 611758bd356ed5..7e9c3baa6a0cd1 100644 --- a/src/coreclr/pal/src/thread/thread.cpp +++ b/src/coreclr/pal/src/thread/thread.cpp @@ -28,6 +28,7 @@ SET_DEFAULT_DEBUG_CHANNEL(THREAD); // some headers have code with asserts, so do #include "pal/virtual.h" #include +#include #if defined(__NetBSD__) && !HAVE_PTHREAD_GETCPUCLOCKID #include @@ -1392,10 +1393,10 @@ CPalThread::ThreadEntry( // - https://forum.snapcraft.io/t/requesting-autoconnect-for-interfaces-in-pigmeat-process-control-home/17987/13 { - int configuredCpuCount = sysconf(_SC_NPROCESSORS_CONF); + int configuredCpuCount = minipal_get_cpu_max_possible_count(); if (configuredCpuCount == -1) { - // In the unlikely event that sysconf(_SC_NPROCESSORS_CONF) fails, just assume a reasonable default maximum number of CPUs to avoid failing thread creation. + // In the unlikely event that minipal_get_cpu_max_possible_count() fails, just assume a reasonable default maximum number of CPUs to avoid failing thread creation. configuredCpuCount = CPU_SETSIZE; } diff --git a/src/native/minipal/CMakeLists.txt b/src/native/minipal/CMakeLists.txt index af1990a9909fc4..25bdd2c0451538 100644 --- a/src/native/minipal/CMakeLists.txt +++ b/src/native/minipal/CMakeLists.txt @@ -24,6 +24,10 @@ if(NOT WIN32 AND NOT CLR_CMAKE_TARGET_ARCH_WASM AND NOT HOST_WASM) list(APPEND SOURCES ospagesize.c) endif() +if(CLR_CMAKE_HOST_UNIX) + list(APPEND SOURCES cpucount.c) +endif() + # Provide an object library for scenarios where we ship static libraries include_directories(${CLR_SRC_NATIVE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/src/native/minipal/cpucount.c b/src/native/minipal/cpucount.c new file mode 100644 index 00000000000000..0298724a8b7d65 --- /dev/null +++ b/src/native/minipal/cpucount.c @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "cpucount.h" + +#include +#include + +int minipal_get_cpu_max_possible_count(void) +{ +#if defined(__linux__) + FILE* f = fopen("/sys/devices/system/cpu/possible", "r"); + if (f != NULL) + { + int maxCpu = -1; + for (;;) + { + int lo, hi; + int matched = fscanf(f, "%d-%d", &lo, &hi); + if (matched == 1) + { + hi = lo; + } + else if (matched != 2) + { + break; + } + + if (maxCpu < hi) + { + maxCpu = hi; + } + + int ch = fgetc(f); + if (ch == EOF || ch != ',') + { + break; + } + } + fclose(f); + if (maxCpu != -1) + { + return maxCpu + 1; + } + } +#endif + + return (int)sysconf(_SC_NPROCESSORS_CONF); +} diff --git a/src/native/minipal/cpucount.h b/src/native/minipal/cpucount.h new file mode 100644 index 00000000000000..5b2d6afa59bf33 --- /dev/null +++ b/src/native/minipal/cpucount.h @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef HAVE_MINIPAL_CPUCOUNT_H +#define HAVE_MINIPAL_CPUCOUNT_H + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + +// Returns the maximum number of CPUs that could ever be available on this system, +// suitable for sizing cpu_set_t allocations via CPU_ALLOC. +// +// On Linux, this reads /sys/devices/system/cpu/possible to account for CPU hotplug. +// This may be larger than the number of online or present CPUs. +// Falls back to sysconf(_SC_NPROCESSORS_CONF) if the sysfs file is unavailable. +int minipal_get_cpu_max_possible_count(void); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // HAVE_MINIPAL_CPUCOUNT_H