From 766f59c2e5bafcc381cf91d2cc619e00f30ee84e Mon Sep 17 00:00:00 2001 From: Manish Godse <61718172+mangod9@users.noreply.github.com> Date: Tue, 25 Oct 2022 18:15:30 -0700 Subject: [PATCH 1/2] build coreclr using gc_pal on windows Also removes NUMA related code from coreclr PAL --- .../dlls/mscoree/coreclr/CMakeLists.txt | 3 +- src/coreclr/gc/CMakeLists.txt | 10 +- src/coreclr/gc/unix/CMakeLists.txt | 2 +- src/coreclr/gc/windows/CMakeLists.txt | 9 + src/coreclr/gc/windows/gcenv.windows.cpp | 12 +- src/coreclr/inc/utilcode.h | 25 - src/coreclr/pal/inc/pal.h | 28 - src/coreclr/utilcode/util.cpp | 88 - src/coreclr/vm/CMakeLists.txt | 1 - src/coreclr/vm/ceemain.cpp | 5 - src/coreclr/vm/gcenv.os.cpp | 1483 ----------------- 11 files changed, 21 insertions(+), 1645 deletions(-) create mode 100644 src/coreclr/gc/windows/CMakeLists.txt delete mode 100644 src/coreclr/vm/gcenv.os.cpp diff --git a/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt b/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt index 6e22bd96f79dc1..cc23c118a0ce37 100644 --- a/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt +++ b/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt @@ -82,7 +82,6 @@ set_property(TARGET coreclr APPEND_STRING PROPERTY LINK_DEPENDS ${EXPORTS_FILE}) if (CLR_CMAKE_HOST_UNIX) set(LIB_UNWINDER unwinder_wks) - set(GC_PAL gc_unix) endif (CLR_CMAKE_HOST_UNIX) # IMPORTANT! Please do not rearrange the order of the libraries. The linker on Linux is @@ -109,7 +108,7 @@ set(CORECLR_LIBRARIES System.Globalization.Native-Static interop coreclrminipal - ${GC_PAL} + gc_pal ) if(CLR_CMAKE_TARGET_WIN32) diff --git a/src/coreclr/gc/CMakeLists.txt b/src/coreclr/gc/CMakeLists.txt index 4441c41df36f94..68002f6791f1b4 100644 --- a/src/coreclr/gc/CMakeLists.txt +++ b/src/coreclr/gc/CMakeLists.txt @@ -29,12 +29,10 @@ set(GC_SOURCES if(CLR_CMAKE_HOST_UNIX) add_subdirectory(unix) include(unix/configure.cmake) - set (GC_SOURCES - ${GC_SOURCES}) else() + add_subdirectory(windows) set (GC_SOURCES ${GC_SOURCES} - windows/gcenv.windows.cpp windows/Native.rc) endif(CLR_CMAKE_HOST_UNIX) @@ -98,9 +96,9 @@ if(CLR_CMAKE_HOST_WIN32) ${STATIC_MT_VCRT_LIB} kernel32.lib advapi32.lib) -else() - set (GC_LINK_LIBRARIES gc_unix) -endif(CLR_CMAKE_HOST_WIN32) +endif(CLR_CMAKE_HOST_WIN32) + +set (GC_LINK_LIBRARIES ${GC_LINK_LIBRARIES} gc_pal) list(APPEND GC_SOURCES ${GC_HEADERS}) diff --git a/src/coreclr/gc/unix/CMakeLists.txt b/src/coreclr/gc/unix/CMakeLists.txt index e1d3571f4479b5..e8915fb1b7ddfb 100644 --- a/src/coreclr/gc/unix/CMakeLists.txt +++ b/src/coreclr/gc/unix/CMakeLists.txt @@ -8,4 +8,4 @@ set(GC_PAL_SOURCES events.cpp cgroup.cpp) -add_library(gc_unix STATIC ${GC_PAL_SOURCES} ${VERSION_FILE_PATH}) +add_library(gc_pal STATIC ${GC_PAL_SOURCES} ${VERSION_FILE_PATH}) diff --git a/src/coreclr/gc/windows/CMakeLists.txt b/src/coreclr/gc/windows/CMakeLists.txt new file mode 100644 index 00000000000000..3ac760254b454b --- /dev/null +++ b/src/coreclr/gc/windows/CMakeLists.txt @@ -0,0 +1,9 @@ +set(CMAKE_INCLUDE_CURRENT_DIR ON) +include_directories("../env") +include_directories("..") + +set(GC_PAL_SOURCES + gcenv.windows.cpp +) + +add_library(gc_pal STATIC ${GC_PAL_SOURCES} Native.rc) diff --git a/src/coreclr/gc/windows/gcenv.windows.cpp b/src/coreclr/gc/windows/gcenv.windows.cpp index 45c242078a6aef..7826be5d3658c7 100644 --- a/src/coreclr/gc/windows/gcenv.windows.cpp +++ b/src/coreclr/gc/windows/gcenv.windows.cpp @@ -7,12 +7,12 @@ #include #include "windows.h" #include "psapi.h" -#include "env/gcenv.structs.h" -#include "env/gcenv.base.h" -#include "env/gcenv.os.h" -#include "env/gcenv.ee.h" -#include "env/gcenv.windows.inl" -#include "env/volatile.h" +#include "gcenv.structs.h" +#include "gcenv.base.h" +#include "gcenv.os.h" +#include "gcenv.ee.h" +#include "gcenv.windows.inl" +#include "volatile.h" #include "gcconfig.h" GCSystemInfo g_SystemInfo; diff --git a/src/coreclr/inc/utilcode.h b/src/coreclr/inc/utilcode.h index 5e6e9452c233ff..df8ece89450dfb 100644 --- a/src/coreclr/inc/utilcode.h +++ b/src/coreclr/inc/utilcode.h @@ -950,31 +950,6 @@ BYTE * ClrVirtualAllocWithinRange(const BYTE *pMinAddr, // LPVOID ClrVirtualAllocAligned(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect, SIZE_T alignment); -class NumaNodeInfo -{ -private: - static BOOL m_enableGCNumaAware; - static uint16_t m_nNodes; - static BOOL InitNumaNodeInfoAPI(); - -public: - static BOOL CanEnableGCNumaAware(); - static void InitNumaNodeInfo(); - -#if !defined(FEATURE_NATIVEAOT) -public: // functions - - static LPVOID VirtualAllocExNuma(HANDLE hProc, LPVOID lpAddr, SIZE_T size, - DWORD allocType, DWORD prot, DWORD node); -#ifdef HOST_WINDOWS - static BOOL GetNumaProcessorNodeEx(PPROCESSOR_NUMBER proc_no, PUSHORT node_no); - static bool GetNumaInfo(PUSHORT total_nodes, DWORD* max_procs_per_node); -#else // HOST_WINDOWS - static BOOL GetNumaProcessorNodeEx(USHORT proc_no, PUSHORT node_no); -#endif // HOST_WINDOWS -#endif -}; - #ifdef HOST_WINDOWS struct CPU_Group_Info diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h index e78408eba45086..0815c8c681f71e 100644 --- a/src/coreclr/pal/inc/pal.h +++ b/src/coreclr/pal/inc/pal.h @@ -3914,34 +3914,6 @@ PALAPI GetSystemInfo( OUT LPSYSTEM_INFO lpSystemInfo); -// -// NUMA related APIs -// - -PALIMPORT -BOOL -PALAPI -GetNumaHighestNodeNumber( - OUT PULONG HighestNodeNumber -); - -PALIMPORT -BOOL -PALAPI -PAL_GetNumaProcessorNode(WORD procNo, WORD* node); - -PALIMPORT -LPVOID -PALAPI -VirtualAllocExNuma( - IN HANDLE hProcess, - IN OPTIONAL LPVOID lpAddress, - IN SIZE_T dwSize, - IN DWORD flAllocationType, - IN DWORD flProtect, - IN DWORD nndPreferred -); - PALIMPORT BOOL PALAPI diff --git a/src/coreclr/utilcode/util.cpp b/src/coreclr/utilcode/util.cpp index a331cddf195c9c..0901e06d1f3028 100644 --- a/src/coreclr/utilcode/util.cpp +++ b/src/coreclr/utilcode/util.cpp @@ -514,94 +514,6 @@ BYTE * ClrVirtualAllocWithinRange(const BYTE *pMinAddr, } #ifdef HOST_WINDOWS - -//****************************************************************************** -// NumaNodeInfo -//****************************************************************************** -#if !defined(FEATURE_NATIVEAOT) - -/*static*/ LPVOID NumaNodeInfo::VirtualAllocExNuma(HANDLE hProc, LPVOID lpAddr, SIZE_T dwSize, - DWORD allocType, DWORD prot, DWORD node) -{ - return ::VirtualAllocExNuma(hProc, lpAddr, dwSize, allocType, prot, node); -} - -/*static*/ BOOL NumaNodeInfo::GetNumaProcessorNodeEx(PPROCESSOR_NUMBER proc_no, PUSHORT node_no) -{ - return ::GetNumaProcessorNodeEx(proc_no, node_no); -} -/*static*/ bool NumaNodeInfo::GetNumaInfo(PUSHORT total_nodes, DWORD* max_procs_per_node) -{ - if (m_enableGCNumaAware) - { - DWORD currentProcsOnNode = 0; - for (uint16_t i = 0; i < m_nNodes; i++) - { - GROUP_AFFINITY processorMask; - if (GetNumaNodeProcessorMaskEx(i, &processorMask)) - { - DWORD procsOnNode = 0; - uintptr_t mask = (uintptr_t)processorMask.Mask; - while (mask) - { - procsOnNode++; - mask &= mask - 1; - } - - currentProcsOnNode = max(currentProcsOnNode, procsOnNode); - } - } - - *max_procs_per_node = currentProcsOnNode; - *total_nodes = m_nNodes; - return true; - } - - return false; -} -#else // HOST_WINDOWS -/*static*/ BOOL NumaNodeInfo::GetNumaProcessorNodeEx(USHORT proc_no, PUSHORT node_no) -{ - return PAL_GetNumaProcessorNode(proc_no, node_no); -} -#endif // HOST_WINDOWS -#endif - -#ifdef HOST_WINDOWS -/*static*/ BOOL NumaNodeInfo::m_enableGCNumaAware = FALSE; -/*static*/ uint16_t NumaNodeInfo::m_nNodes = 0; -/*static*/ BOOL NumaNodeInfo::InitNumaNodeInfoAPI() -{ -#if !defined(FEATURE_NATIVEAOT) - //check for numa support if multiple heaps are used - ULONG highest = 0; - - if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_GCNumaAware) == 0) - return FALSE; - - // fail to get the highest numa node number - if (!::GetNumaHighestNodeNumber(&highest) || (highest == 0)) - return FALSE; - - m_nNodes = (USHORT)(highest + 1); - - return TRUE; -#else - return FALSE; -#endif -} - -/*static*/ BOOL NumaNodeInfo::CanEnableGCNumaAware() -{ - return m_enableGCNumaAware; -} - -/*static*/ void NumaNodeInfo::InitNumaNodeInfo() -{ - m_enableGCNumaAware = InitNumaNodeInfoAPI(); -} - - //****************************************************************************** // CPUGroupInfo //****************************************************************************** diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt index 8ac82802a9a41d..5039e61d54db85 100644 --- a/src/coreclr/vm/CMakeLists.txt +++ b/src/coreclr/vm/CMakeLists.txt @@ -506,7 +506,6 @@ endif (CLR_CMAKE_TARGET_ARCH_AMD64 AND CLR_CMAKE_TARGET_WIN32) if (CLR_CMAKE_TARGET_WIN32) set ( GC_SOURCES_WKS ${GC_SOURCES_WKS} - gcenv.os.cpp ) endif (CLR_CMAKE_TARGET_WIN32) diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index a2040948c31ece..daa62ab673fb7b 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -625,11 +625,6 @@ void EEStartupHelper() #ifdef HOST_WINDOWS InitializeCrashDump(); - - // Initialize Numa and CPU group information - // Need to do this as early as possible. Used by creating object handle - // table inside Ref_Initialization() before GC is initialized. - NumaNodeInfo::InitNumaNodeInfo(); #endif // HOST_WINDOWS #ifndef TARGET_UNIX CPUGroupInfo::EnsureInitialized(); diff --git a/src/coreclr/vm/gcenv.os.cpp b/src/coreclr/vm/gcenv.os.cpp deleted file mode 100644 index 4f0868d4f6d692..00000000000000 --- a/src/coreclr/vm/gcenv.os.cpp +++ /dev/null @@ -1,1483 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/* - * gcenv.os.cpp - * - * GCToOSInterface implementation - * - - * - */ - -#include "common.h" -#include "gcenv.h" - -#ifndef TARGET_UNIX -#include -#endif - -#ifdef Sleep -#undef Sleep -#endif // Sleep - -#include "../gc/env/gcenv.os.h" - -#define MAX_PTR ((uint8_t*)(~(ptrdiff_t)0)) - -#ifdef TARGET_UNIX -uint32_t g_pageSizeUnixInl = 0; -#endif - -static AffinitySet g_processAffinitySet; - -class GroupProcNo -{ - uint16_t m_groupProc; - -public: - - static const uint16_t NoGroup = 0; - - GroupProcNo(uint16_t groupProc) : m_groupProc(groupProc) - { - } - - GroupProcNo(uint16_t group, uint16_t procIndex) : m_groupProc((group << 6) | procIndex) - { - // Making this the same as the # of NUMA node we support. - _ASSERTE(group < 0x40); - _ASSERTE(procIndex <= 0x3f); - } - - uint16_t GetGroup() { return m_groupProc >> 6; } - uint16_t GetProcIndex() { return m_groupProc & 0x3f; } - uint16_t GetCombinedValue() { return m_groupProc; } -}; - -#if !defined(TARGET_UNIX) - -static bool g_SeLockMemoryPrivilegeAcquired = false; - -bool InitLargePagesPrivilege() -{ - TOKEN_PRIVILEGES tp; - LUID luid; - if (!LookupPrivilegeValueW(nullptr, SE_LOCK_MEMORY_NAME, &luid)) - { - return false; - } - - tp.PrivilegeCount = 1; - tp.Privileges[0].Luid = luid; - tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - - HANDLE token; - if (!OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token)) - { - return false; - } - - BOOL retVal = AdjustTokenPrivileges(token, FALSE, &tp, 0, nullptr, 0); - DWORD gls = GetLastError(); - CloseHandle(token); - - if (!retVal) - { - return false; - } - - if (gls != 0) - { - return false; - } - - return true; -} - -#endif // TARGET_UNIX - -static void GetProcessMemoryLoad(LPMEMORYSTATUSEX pMSEX) -{ - LIMITED_METHOD_CONTRACT; - - pMSEX->dwLength = sizeof(MEMORYSTATUSEX); - BOOL fRet = GlobalMemoryStatusEx(pMSEX); - _ASSERTE(fRet); -} - -// Initialize the interface implementation -// Return: -// true if it has succeeded, false if it has failed -bool GCToOSInterface::Initialize() -{ - LIMITED_METHOD_CONTRACT; - -#ifdef TARGET_UNIX - g_pageSizeUnixInl = GetOsPageSize(); - - uint32_t currentProcessCpuCount = PAL_GetLogicalCpuCountFromOS(); - if (PAL_GetCurrentThreadAffinitySet(AffinitySet::BitsetDataSize, (UINT_PTR *)g_processAffinitySet.GetBitsetData())) - { - _ASSERTE(currentProcessCpuCount == g_processAffinitySet.Count()); - } - else - { - // There is no way to get affinity on the current OS, set the affinity set to reflect all processors - for (size_t i = 0; i < currentProcessCpuCount; i++) - { - g_processAffinitySet.Add(i); - } - } -#else // TARGET_UNIX - if (CPUGroupInfo::CanEnableGCCPUGroups()) - { - // When CPU groups are enabled, then the process is not bound by the process affinity set at process launch. - // Set the initial affinity mask so that all processors are enabled. - for (size_t i = 0; i < CPUGroupInfo::GetNumActiveProcessors(); i++) - { - g_processAffinitySet.Add(i); - } - } - else - { - // When CPU groups are disabled, the process affinity mask specified at the process launch cannot be - // escaped. - uintptr_t pmask, smask; - if (!!::GetProcessAffinityMask(::GetCurrentProcess(), (PDWORD_PTR)&pmask, (PDWORD_PTR)&smask)) - { - for (size_t i = 0; i < 8 * sizeof(uintptr_t); i++) - { - if ((pmask & ((uintptr_t)1 << i)) != 0) - { - g_processAffinitySet.Add(i); - } - } - } - } -#endif // TARGET_UNIX - - return true; -} - -// Shutdown the interface implementation -void GCToOSInterface::Shutdown() -{ - LIMITED_METHOD_CONTRACT; -} - -// Get numeric id of the current thread if possible on the -// current platform. It is intended for logging purposes only. -// Return: -// Numeric id of the current thread or 0 if the -uint64_t GCToOSInterface::GetCurrentThreadIdForLogging() -{ - LIMITED_METHOD_CONTRACT; - return ::GetCurrentThreadId(); -} - -// Get id of the process -// Return: -// Id of the current process -uint32_t GCToOSInterface::GetCurrentProcessId() -{ - LIMITED_METHOD_CONTRACT; - return ::GetCurrentProcessId(); -} - -// Set ideal processor for the current thread -// Parameters: -// srcProcNo - processor number the thread currently runs on -// dstProcNo - processor number the thread should be migrated to -// Return: -// true if it has succeeded, false if it has failed -bool GCToOSInterface::SetCurrentThreadIdealAffinity(uint16_t srcProcNo, uint16_t dstProcNo) -{ - LIMITED_METHOD_CONTRACT; - - bool success = true; -#ifndef TARGET_UNIX - GroupProcNo srcGroupProcNo(srcProcNo); - GroupProcNo dstGroupProcNo(dstProcNo); - - PROCESSOR_NUMBER proc; - - if (CPUGroupInfo::CanEnableGCCPUGroups()) - { - if (srcGroupProcNo.GetGroup() != dstGroupProcNo.GetGroup()) - { - //only set ideal processor when srcProcNo and dstProcNo are in the same cpu - //group. DO NOT MOVE THREADS ACROSS CPU GROUPS - return true; - } - - proc.Group = (WORD)dstGroupProcNo.GetGroup(); - proc.Number = (BYTE)dstGroupProcNo.GetProcIndex(); - proc.Reserved = 0; - - success = !!SetThreadIdealProcessorEx(GetCurrentThread (), &proc, NULL); - } - else - { - if (GetThreadIdealProcessorEx(GetCurrentThread(), &proc)) - { - proc.Number = (BYTE)dstGroupProcNo.GetProcIndex(); - success = !!SetThreadIdealProcessorEx(GetCurrentThread(), &proc, &proc); - } - } - - return success; - -#else // !TARGET_UNIX - - // There is no way to set a thread ideal processor on Unix, so do nothing. - return true; - -#endif // !TARGET_UNIX -} - -bool GCToOSInterface::GetCurrentThreadIdealProc(uint16_t* procNo) -{ - LIMITED_METHOD_CONTRACT; - bool success = false; -#ifndef TARGET_UNIX - PROCESSOR_NUMBER proc; - success = !!GetThreadIdealProcessorEx(GetCurrentThread(), &proc); - if (success) - { - GroupProcNo groupProcNo(proc.Group, proc.Number); - *procNo = groupProcNo.GetCombinedValue(); - } -#endif //TARGET_UNIX - return success; -} - -// Get the number of the current processor -uint32_t GCToOSInterface::GetCurrentProcessorNumber() -{ - LIMITED_METHOD_CONTRACT; - - _ASSERTE(CanGetCurrentProcessorNumber()); - -#ifndef TARGET_UNIX - PROCESSOR_NUMBER proc_no_cpu_group; - GetCurrentProcessorNumberEx(&proc_no_cpu_group); - - GroupProcNo groupProcNo(proc_no_cpu_group.Group, proc_no_cpu_group.Number); - return groupProcNo.GetCombinedValue(); -#else - return ::GetCurrentProcessorNumber(); -#endif //!TARGET_UNIX -} - -// Check if the OS supports getting current processor number -bool GCToOSInterface::CanGetCurrentProcessorNumber() -{ - LIMITED_METHOD_CONTRACT; - -#ifdef TARGET_UNIX - return PAL_HasGetCurrentProcessorNumber(); -#else - // on all Windows platforms we support this API exists - return true; -#endif -} - -// Flush write buffers of processors that are executing threads of the current process -void GCToOSInterface::FlushProcessWriteBuffers() -{ - LIMITED_METHOD_CONTRACT; - ::FlushProcessWriteBuffers(); -} - -// Break into a debugger -void GCToOSInterface::DebugBreak() -{ - LIMITED_METHOD_CONTRACT; - ::DebugBreak(); -} - -// Causes the calling thread to sleep for the specified number of milliseconds -// Parameters: -// sleepMSec - time to sleep before switching to another thread -void GCToOSInterface::Sleep(uint32_t sleepMSec) -{ - LIMITED_METHOD_CONTRACT; - __SwitchToThread(sleepMSec, 0); -} - -// Causes the calling thread to yield execution to another thread that is ready to run on the current processor. -// Parameters: -// switchCount - number of times the YieldThread was called in a loop -void GCToOSInterface::YieldThread(uint32_t switchCount) -{ - LIMITED_METHOD_CONTRACT; - __SwitchToThread(0, switchCount); -} - -// Reserve virtual memory range. -// Parameters: -// address - starting virtual address, it can be NULL to let the function choose the starting address -// size - size of the virtual memory range -// alignment - requested memory alignment -// flags - flags to control special settings like write watching -// node - the NUMA node to reserve memory on -// Return: -// Starting virtual address of the reserved range -void* GCToOSInterface::VirtualReserve(size_t size, size_t alignment, uint32_t flags, uint16_t node) -{ - LIMITED_METHOD_CONTRACT; - - DWORD memFlags = (flags & VirtualReserveFlags::WriteWatch) ? (MEM_RESERVE | MEM_WRITE_WATCH) : MEM_RESERVE; - - if (node == NUMA_NODE_UNDEFINED) - { - // This is not strictly necessary for a correctness standpoint. Windows already guarantees - // allocation granularity alignment when using MEM_RESERVE, so aligning the size here has no effect. - // However, ClrVirtualAlloc does expect the size to be aligned to the allocation granularity. - size_t aligned_size = (size + g_SystemInfo.dwAllocationGranularity - 1) & ~static_cast(g_SystemInfo.dwAllocationGranularity - 1); - if (alignment == 0) - { - return ::ClrVirtualAlloc (0, aligned_size, memFlags, PAGE_READWRITE); - } - else - { - return ::ClrVirtualAllocAligned (0, aligned_size, memFlags, PAGE_READWRITE, alignment); - } - } - else - { - return NumaNodeInfo::VirtualAllocExNuma (::GetCurrentProcess (), NULL, size, memFlags, PAGE_READWRITE, node); - } -} - -// Release virtual memory range previously reserved using VirtualReserve -// Parameters: -// address - starting virtual address -// size - size of the virtual memory range -// Return: -// true if it has succeeded, false if it has failed -bool GCToOSInterface::VirtualRelease(void* address, size_t size) -{ - LIMITED_METHOD_CONTRACT; - - UNREFERENCED_PARAMETER(size); - return !!::ClrVirtualFree(address, 0, MEM_RELEASE); -} - -// Commit virtual memory range. -// Parameters: -// size - size of the virtual memory range -// Return: -// Starting virtual address of the committed range -void* GCToOSInterface::VirtualReserveAndCommitLargePages(size_t size, uint16_t node) -{ - LIMITED_METHOD_CONTRACT; - -#if !defined(TARGET_UNIX) - if (!g_SeLockMemoryPrivilegeAcquired) - { - if (!InitLargePagesPrivilege()) - { - return nullptr; - } - - g_SeLockMemoryPrivilegeAcquired = true; - } - - SIZE_T largePageMinimum = GetLargePageMinimum(); - size = (size + (largePageMinimum - 1)) & ~(largePageMinimum - 1); -#endif - - if (node == NUMA_NODE_UNDEFINED) - { - return ::ClrVirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE); - } - else - { - return NumaNodeInfo::VirtualAllocExNuma(::GetCurrentProcess(), nullptr, size, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE, node); - } -} - -// Commit virtual memory range. It must be part of a range reserved using VirtualReserve. -// Parameters: -// address - starting virtual address -// size - size of the virtual memory range -// Return: -// true if it has succeeded, false if it has failed -bool GCToOSInterface::VirtualCommit(void* address, size_t size, uint16_t node) -{ - LIMITED_METHOD_CONTRACT; - - if (node == NUMA_NODE_UNDEFINED) - { - return ::ClrVirtualAlloc(address, size, MEM_COMMIT, PAGE_READWRITE) != NULL; - } - else - { - return NumaNodeInfo::VirtualAllocExNuma(::GetCurrentProcess(), address, size, MEM_COMMIT, PAGE_READWRITE, node) != NULL; - } -} - -// Decomit virtual memory range. -// Parameters: -// address - starting virtual address -// size - size of the virtual memory range -// Return: -// true if it has succeeded, false if it has failed -bool GCToOSInterface::VirtualDecommit(void* address, size_t size) -{ - LIMITED_METHOD_CONTRACT; - - return !!::ClrVirtualFree(address, size, MEM_DECOMMIT); -} - -// Reset virtual memory range. Indicates that data in the memory range specified by address and size is no -// longer of interest, but it should not be decommitted. -// Parameters: -// address - starting virtual address -// size - size of the virtual memory range -// unlock - true if the memory range should also be unlocked -// Return: -// true if it has succeeded, false if it has failed -bool GCToOSInterface::VirtualReset(void * address, size_t size, bool unlock) -{ - LIMITED_METHOD_CONTRACT; - - bool success = ::ClrVirtualAlloc(address, size, MEM_RESET, PAGE_READWRITE) != NULL; -#ifndef TARGET_UNIX - if (success && unlock) - { - // Remove the page range from the working set - ::VirtualUnlock(address, size); - } -#endif // TARGET_UNIX - - return success; -} - -// Check if the OS supports write watching -bool GCToOSInterface::SupportsWriteWatch() -{ - LIMITED_METHOD_CONTRACT; - -#ifndef TARGET_UNIX - bool writeWatchSupported = false; - - // check if the OS supports write-watch. - // Drawbridge does not support write-watch so we still need to do the runtime detection for them. - // Otherwise, all currently supported OSes do support write-watch. - void* mem = VirtualReserve(g_SystemInfo.dwAllocationGranularity, 0, VirtualReserveFlags::WriteWatch); - if (mem != NULL) - { - VirtualRelease (mem, g_SystemInfo.dwAllocationGranularity); - writeWatchSupported = true; - } - - return writeWatchSupported; -#else // TARGET_UNIX - return false; -#endif // TARGET_UNIX -} - -// Reset the write tracking state for the specified virtual memory range. -// Parameters: -// address - starting virtual address -// size - size of the virtual memory range -void GCToOSInterface::ResetWriteWatch(void* address, size_t size) -{ - LIMITED_METHOD_CONTRACT; - -#ifndef TARGET_UNIX - ::ResetWriteWatch(address, size); -#endif // TARGET_UNIX -} - -// Retrieve addresses of the pages that are written to in a region of virtual memory -// Parameters: -// resetState - true indicates to reset the write tracking state -// address - starting virtual address -// size - size of the virtual memory range -// pageAddresses - buffer that receives an array of page addresses in the memory region -// pageAddressesCount - on input, size of the lpAddresses array, in array elements -// on output, the number of page addresses that are returned in the array. -// Return: -// true if it has succeeded, false if it has failed -bool GCToOSInterface::GetWriteWatch(bool resetState, void* address, size_t size, void** pageAddresses, uintptr_t* pageAddressesCount) -{ - LIMITED_METHOD_CONTRACT; - -#ifndef TARGET_UNIX - uint32_t flags = resetState ? 1 : 0; - ULONG granularity; - - bool success = ::GetWriteWatch(flags, address, size, pageAddresses, (ULONG_PTR*)pageAddressesCount, &granularity) == 0; - _ASSERTE (granularity == GetOsPageSize()); - - return success; -#else // TARGET_UNIX - *pageAddresses = NULL; - *pageAddressesCount = 0; - - return true; -#endif // TARGET_UNIX -} - -#ifdef TARGET_WINDOWS - -// This function checks to see if GetLogicalProcessorInformation API is supported. -// On success, this function allocates a SLPI array, sets nEntries to number -// of elements in the SLPI array and returns a pointer to the SLPI array after filling it with information. -// -// Note: If successful, IsGLPISupported allocates memory for the SLPI array and expects the caller to -// free the memory once the caller is done using the information in the SLPI array. -// -// If the API is not supported or any failure, returns NULL -// -SYSTEM_LOGICAL_PROCESSOR_INFORMATION *IsGLPISupported( PDWORD nEntries ) -{ - DWORD cbslpi = 0; - DWORD dwNumElements = 0; - SYSTEM_LOGICAL_PROCESSOR_INFORMATION *pslpi = NULL; - - // We setup the first call to GetLogicalProcessorInformation to fail so that we can obtain - // the size of the buffer required to allocate for the SLPI array that is returned - - if (!GetLogicalProcessorInformation(pslpi, &cbslpi) && - GetLastError() != ERROR_INSUFFICIENT_BUFFER) - { - // If we fail with anything other than an ERROR_INSUFFICIENT_BUFFER here, we punt with failure. - return NULL; - } - - _ASSERTE(cbslpi); - - // compute the number of SLPI entries required to hold the information returned from GLPI - - dwNumElements = cbslpi / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); - - // allocate a buffer in the free heap to hold an array of SLPI entries from GLPI, number of elements in the array is dwNumElements - - pslpi = new (nothrow) SYSTEM_LOGICAL_PROCESSOR_INFORMATION[ dwNumElements ]; - - if(pslpi == NULL) - { - // the memory allocation failed - return NULL; - } - - // Make call to GetLogicalProcessorInformation. Returns array of SLPI structures - - if (!GetLogicalProcessorInformation(pslpi, &cbslpi)) - { - // GetLogicalProcessorInformation failed - delete[] pslpi ; //Allocation was fine but the API call itself failed and so we are releasing the memory before the return NULL. - return NULL ; - } - - // GetLogicalProcessorInformation successful, set nEntries to number of entries in the SLPI array - *nEntries = dwNumElements; - - return pslpi; // return pointer to SLPI array -} - -// This function returns the size of highest level cache on the physical chip. If it cannot -// determine the cachesize this function returns 0. -size_t GetLogicalProcessorCacheSizeFromOS() -{ - size_t cache_size = 0; - size_t cache_level = 0; - DWORD nEntries = 0; - - // Try to use GetLogicalProcessorInformation API and get a valid pointer to the SLPI array if successful. Returns NULL - // if API not present or on failure. - - SYSTEM_LOGICAL_PROCESSOR_INFORMATION *pslpi = IsGLPISupported(&nEntries) ; - - if (pslpi == NULL) - { - // GetLogicalProcessorInformation not supported or failed. - goto Exit; - } - - // Crack the information. Iterate through all the SLPI array entries for all processors in system. - // Will return the greatest of all the processor cache sizes or zero - { - size_t last_cache_size = 0; - - for (DWORD i=0; i < nEntries; i++) - { - if (pslpi[i].Relationship == RelationCache) - { - if (last_cache_size < pslpi[i].Cache.Size) - { - last_cache_size = pslpi[i].Cache.Size; - cache_level = pslpi[i].Cache.Level; - } - } - } - cache_size = last_cache_size; - } - -Exit: - if(pslpi) - delete[] pslpi; // release the memory allocated for the SLPI array. - -#if defined(TARGET_ARM64) - if (cache_level != 3) - { - uint32_t totalCPUCount = GCToOSInterface::GetTotalProcessorCount(); - - // We expect to get the L3 cache size for Arm64 but currently expected to be missing that info - // from most of the machines. - // Hence, just use the following heuristics at best depending on the CPU count - // 1 ~ 4 : 4 MB - // 5 ~ 16 : 8 MB - // 17 ~ 64 : 16 MB - // 65+ : 32 MB - if (totalCPUCount < 5) - { - cache_size = 4; - } - else if (totalCPUCount < 17) - { - cache_size = 8; - } - else if (totalCPUCount < 65) - { - cache_size = 16; - } - else - { - cache_size = 32; - } - - cache_size *= (1024 * 1024); - } -#endif // TARGET_ARM64 - - return cache_size; -} - -#endif // TARGET_WINDOWS - -// Get size of the largest cache on the processor die -// Parameters: -// trueSize - true to return true cache size, false to return scaled up size based on -// the processor architecture -// Return: -// Size of the cache -size_t GCToOSInterface::GetCacheSizePerLogicalCpu(bool trueSize) -{ - LIMITED_METHOD_CONTRACT; - - static volatile size_t s_maxSize; - static volatile size_t s_maxTrueSize; - - size_t size = trueSize ? s_maxTrueSize : s_maxSize; - if (size != 0) - return size; - - size_t maxSize, maxTrueSize; - - maxSize = maxTrueSize = GetLogicalProcessorCacheSizeFromOS() ; // Returns the size of the highest level processor cache - - s_maxSize = maxSize; - s_maxTrueSize = maxTrueSize; - - // printf("GetCacheSizePerLogicalCpu returns %zu, adjusted size %zu\n", maxSize, maxTrueSize); - return trueSize ? maxTrueSize : maxSize; -} - -// Sets the calling thread's affinity to only run on the processor specified -// Parameters: -// procNo - The requested processor for the calling thread. -// Return: -// true if setting the affinity was successful, false otherwise. -bool GCToOSInterface::SetThreadAffinity(uint16_t procNo) -{ - LIMITED_METHOD_CONTRACT; -#ifndef TARGET_UNIX - GroupProcNo groupProcNo(procNo); - - if (CPUGroupInfo::CanEnableGCCPUGroups()) - { - GROUP_AFFINITY ga; - ga.Group = (WORD)groupProcNo.GetGroup(); - ga.Reserved[0] = 0; // reserve must be filled with zero - ga.Reserved[1] = 0; // otherwise call may fail - ga.Reserved[2] = 0; - ga.Mask = (size_t)1 << groupProcNo.GetProcIndex(); - return !!SetThreadGroupAffinity(GetCurrentThread(), &ga, nullptr); - } - else - { - return !!SetThreadAffinityMask(GetCurrentThread(), (DWORD_PTR)1 << groupProcNo.GetProcIndex()); - } -#else // TARGET_UNIX - return PAL_SetCurrentThreadAffinity(procNo); -#endif // TARGET_UNIX -} - -// Boosts the calling thread's thread priority to a level higher than the default -// for new threads. -// Parameters: -// None. -// Return: -// true if the priority boost was successful, false otherwise. -bool GCToOSInterface::BoostThreadPriority() -{ - return !!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); -} - -// Set the set of processors enabled for GC threads for the current process based on config specified affinity mask and set -// Parameters: -// configAffinityMask - mask specified by the GCHeapAffinitizeMask config -// configAffinitySet - affinity set specified by the GCHeapAffinitizeRanges config -// Return: -// set of enabled processors -const AffinitySet* GCToOSInterface::SetGCThreadsAffinitySet(uintptr_t configAffinityMask, const AffinitySet* configAffinitySet) -{ -#ifndef TARGET_UNIX - if (CPUGroupInfo::CanEnableGCCPUGroups()) -#endif // !TARGET_UNIX - { - if (!configAffinitySet->IsEmpty()) - { - // Update the process affinity set using the configured set - for (size_t i = 0; i < MAX_SUPPORTED_CPUS; i++) - { - if (g_processAffinitySet.Contains(i) && !configAffinitySet->Contains(i)) - { - g_processAffinitySet.Remove(i); - } - } - } - } -#ifndef TARGET_UNIX - else - { - if (configAffinityMask != 0) - { - // Update the process affinity set using the configured mask - for (size_t i = 0; i < 8 * sizeof(uintptr_t); i++) - { - if (g_processAffinitySet.Contains(i) && ((configAffinityMask & ((uintptr_t)1 << i)) == 0)) - { - g_processAffinitySet.Remove(i); - } - } - } - } -#endif // !TARGET_UNIX - - return &g_processAffinitySet; -} - -// Return the size of the user-mode portion of the virtual address space of this process. -// Return: -// non zero if it has succeeded, (size_t)-1 if not available -size_t GCToOSInterface::GetVirtualMemoryLimit() -{ - LIMITED_METHOD_CONTRACT; - - MEMORYSTATUSEX memStatus; - GetProcessMemoryLoad(&memStatus); - - return (size_t)memStatus.ullTotalVirtual; -} - -static size_t g_RestrictedPhysicalMemoryLimit = (size_t)MAX_PTR; - -#ifndef TARGET_UNIX - -static size_t GetRestrictedPhysicalMemoryLimit() -{ - LIMITED_METHOD_CONTRACT; - - // The limit was cached already - if (g_RestrictedPhysicalMemoryLimit != (size_t)MAX_PTR) - return g_RestrictedPhysicalMemoryLimit; - - size_t job_physical_memory_limit = (size_t)MAX_PTR; - uint64_t total_virtual = 0; - uint64_t total_physical = 0; - BOOL in_job_p = FALSE; - - if (!IsProcessInJob(GetCurrentProcess(), NULL, &in_job_p)) - goto exit; - - if (in_job_p) - { - JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info; - if (QueryInformationJobObject(NULL, JobObjectExtendedLimitInformation, &limit_info, - sizeof(limit_info), NULL)) - { - size_t job_memory_limit = (size_t)MAX_PTR; - size_t job_process_memory_limit = (size_t)MAX_PTR; - size_t job_workingset_limit = (size_t)MAX_PTR; - - // Notes on the NT job object: - // - // You can specific a bigger process commit or working set limit than - // job limit which is pointless so we use the smallest of all 3 as - // to calculate our "physical memory load" or "available physical memory" - // when running inside a job object, ie, we treat this as the amount of physical memory - // our process is allowed to use. - // - // The commit limit is already reflected by default when you run in a - // job but the physical memory load is not. - // - if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_JOB_MEMORY) != 0) - job_memory_limit = limit_info.JobMemoryLimit; - if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_PROCESS_MEMORY) != 0) - job_process_memory_limit = limit_info.ProcessMemoryLimit; - if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_WORKINGSET) != 0) - job_workingset_limit = limit_info.BasicLimitInformation.MaximumWorkingSetSize; - - if ((job_memory_limit != (size_t)MAX_PTR) || - (job_process_memory_limit != (size_t)MAX_PTR) || - (job_workingset_limit != (size_t)MAX_PTR)) - { - job_physical_memory_limit = min (job_memory_limit, job_process_memory_limit); - job_physical_memory_limit = min (job_physical_memory_limit, job_workingset_limit); - - MEMORYSTATUSEX ms; - GetProcessMemoryLoad(&ms); - total_virtual = ms.ullTotalVirtual; - total_physical = ms.ullAvailPhys; - - // A sanity check in case someone set a larger limit than there is actual physical memory. - job_physical_memory_limit = (size_t) min (job_physical_memory_limit, ms.ullTotalPhys); - } - } - } - -exit: - if (job_physical_memory_limit == (size_t)MAX_PTR) - { - job_physical_memory_limit = 0; - } - - // Check to see if we are limited by VM. - if (total_virtual == 0) - { - MEMORYSTATUSEX ms; - GetProcessMemoryLoad(&ms); - - total_virtual = ms.ullTotalVirtual; - total_physical = ms.ullTotalPhys; - } - - if (job_physical_memory_limit != 0) - { - total_physical = job_physical_memory_limit; - } - - if (total_virtual < total_physical) - { - // Limited by virtual address space - job_physical_memory_limit = 0; - } - - VolatileStore(&g_RestrictedPhysicalMemoryLimit, job_physical_memory_limit); - return g_RestrictedPhysicalMemoryLimit; -} - -#else - -static size_t GetRestrictedPhysicalMemoryLimit() -{ - LIMITED_METHOD_CONTRACT; - - // The limit was cached already - if (g_RestrictedPhysicalMemoryLimit != (size_t)MAX_PTR) - return g_RestrictedPhysicalMemoryLimit; - - size_t memory_limit = PAL_GetRestrictedPhysicalMemoryLimit(); - - VolatileStore(&g_RestrictedPhysicalMemoryLimit, memory_limit); - return g_RestrictedPhysicalMemoryLimit; -} -#endif // TARGET_UNIX - -// Get the physical memory that this process can use. -// Return: -// non zero if it has succeeded, 0 if it has failed -uint64_t GCToOSInterface::GetPhysicalMemoryLimit(bool* is_restricted) -{ - LIMITED_METHOD_CONTRACT; - - if (is_restricted) - *is_restricted = false; - - size_t restricted_limit = GetRestrictedPhysicalMemoryLimit(); - if (restricted_limit != 0) - { - if (is_restricted) - *is_restricted = true; - - return restricted_limit; - } - - MEMORYSTATUSEX memStatus; - GetProcessMemoryLoad(&memStatus); - -#ifndef TARGET_UNIX - // For 32-bit processes the virtual address range could be smaller than the amount of physical - // memory on the machine/in the container, we need to restrict by the VM. - if (memStatus.ullTotalVirtual < memStatus.ullTotalPhys) - return memStatus.ullTotalVirtual; -#endif - - return memStatus.ullTotalPhys; -} - -// Get memory status -// Parameters: -// memory_load - A number between 0 and 100 that specifies the approximate percentage of physical memory -// that is in use (0 indicates no memory use and 100 indicates full memory use). -// available_physical - The amount of physical memory currently available, in bytes. -// available_page_file - The maximum amount of memory the current process can commit, in bytes. -// Remarks: -// Any parameter can be null. -void GCToOSInterface::GetMemoryStatus(uint64_t restricted_limit, uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file) -{ - LIMITED_METHOD_CONTRACT; - - if (restricted_limit != 0) - { - size_t workingSetSize; - BOOL status = FALSE; -#ifndef TARGET_UNIX - PROCESS_MEMORY_COUNTERS pmc; - status = GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)); - workingSetSize = pmc.WorkingSetSize; -#else - status = PAL_GetPhysicalMemoryUsed(&workingSetSize); -#endif - if (status) - { - if (memory_load) - *memory_load = (uint32_t)((float)workingSetSize * 100.0 / (float)restricted_limit); - if (available_physical) - { - if(workingSetSize > restricted_limit) - *available_physical = 0; - else - *available_physical = restricted_limit - workingSetSize; - } - // Available page file doesn't mean much when physical memory is restricted since - // we don't know how much of it is available to this process so we are not going to - // bother to make another OS call for it. - if (available_page_file) - *available_page_file = 0; - - return; - } - } - - MEMORYSTATUSEX ms; - GetProcessMemoryLoad(&ms); - -#ifndef TARGET_UNIX - // For 32-bit processes the virtual address range could be smaller than the amount of physical - // memory on the machine/in the container, we need to restrict by the VM. - if (ms.ullTotalVirtual < ms.ullTotalPhys) - { - if (memory_load != NULL) - *memory_load = (uint32_t)((float)(ms.ullTotalVirtual - ms.ullAvailVirtual) * 100.0 / (float)ms.ullTotalVirtual); - if (available_physical != NULL) - *available_physical = ms.ullTotalVirtual; - - // Available page file isn't helpful when we are restricted by virtual memory - // since the amount of memory we can reserve is less than the amount of - // memory we can commit. - if (available_page_file != NULL) - *available_page_file = 0; - } - else -#endif //!TARGET_UNIX - { - if (memory_load != NULL) - *memory_load = ms.dwMemoryLoad; - if (available_physical != NULL) - *available_physical = ms.ullAvailPhys; - if (available_page_file != NULL) - *available_page_file = ms.ullAvailPageFile; - } -} - -// Get a high precision performance counter -// Return: -// The counter value -int64_t GCToOSInterface::QueryPerformanceCounter() -{ - LIMITED_METHOD_CONTRACT; - - LARGE_INTEGER ts; - if (!::QueryPerformanceCounter(&ts)) - { - DebugBreak(); - _ASSERTE(!"Fatal Error - cannot query performance counter."); - EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); // TODO: fatal error - } - - return ts.QuadPart; -} - -// Get a frequency of the high precision performance counter -// Return: -// The counter frequency -int64_t GCToOSInterface::QueryPerformanceFrequency() -{ - LIMITED_METHOD_CONTRACT; - - LARGE_INTEGER frequency; - if (!::QueryPerformanceFrequency(&frequency)) - { - DebugBreak(); - _ASSERTE(!"Fatal Error - cannot query performance counter."); - EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); // TODO: fatal error - } - - return frequency.QuadPart; -} - -// Get a time stamp with a low precision -// Return: -// Time stamp in milliseconds -uint64_t GCToOSInterface::GetLowPrecisionTimeStamp() -{ - LIMITED_METHOD_CONTRACT; - - return ::GetTickCount64(); -} - -uint32_t GCToOSInterface::GetTotalProcessorCount() -{ - LIMITED_METHOD_CONTRACT; - - return ::GetTotalProcessorCount(); -} - -bool GCToOSInterface::CanEnableGCNumaAware() -{ - LIMITED_METHOD_CONTRACT; - - return NumaNodeInfo::CanEnableGCNumaAware() != FALSE; -} - -bool GCToOSInterface::GetNumaInfo(uint16_t* total_nodes, uint32_t* max_procs_per_node) -{ -#ifndef TARGET_UNIX - return NumaNodeInfo::GetNumaInfo(total_nodes, (DWORD*)max_procs_per_node); -#else - return false; -#endif //!TARGET_UNIX -} - -bool GCToOSInterface::GetCPUGroupInfo(uint16_t* total_groups, uint32_t* max_procs_per_group) -{ -#ifndef TARGET_UNIX - return CPUGroupInfo::GetCPUGroupInfo(total_groups, (DWORD*)max_procs_per_group); -#else - return false; -#endif //!TARGET_UNIX -} - -bool GCToOSInterface::CanEnableGCCPUGroups() -{ - LIMITED_METHOD_CONTRACT; -#ifndef TARGET_UNIX - return CPUGroupInfo::CanEnableGCCPUGroups() != FALSE; -#else - return false; -#endif //!TARGET_UNIX -} - -// Get processor number and optionally its NUMA node number for the specified heap number -// Parameters: -// heap_number - heap number to get the result for -// proc_no - set to the selected processor number -// node_no - set to the NUMA node of the selected processor or to NUMA_NODE_UNDEFINED -// Return: -// true if it succeeded -bool GCToOSInterface::GetProcessorForHeap(uint16_t heap_number, uint16_t* proc_no, uint16_t* node_no) -{ - bool success = false; - - // Locate heap_number-th available processor - uint16_t procIndex = 0; - size_t cnt = heap_number; - for (uint16_t i = 0; i < MAX_SUPPORTED_CPUS; i++) - { - if (g_processAffinitySet.Contains(i)) - { - if (cnt == 0) - { - procIndex = i; - success = true; - break; - } - - cnt--; - } - } - - if (success) - { -#ifndef TARGET_UNIX - WORD gn, gpn; - - if (CPUGroupInfo::CanEnableGCCPUGroups()) - { - CPUGroupInfo::GetGroupForProcessor(procIndex, &gn, &gpn); - } - else - { - gn = GroupProcNo::NoGroup; - gpn = procIndex; - } - - GroupProcNo groupProcNo(gn, gpn); - *proc_no = groupProcNo.GetCombinedValue(); - - PROCESSOR_NUMBER procNumber; - - if (CPUGroupInfo::CanEnableGCCPUGroups()) - { - procNumber.Group = gn; - } - else - { - // Get the current processor group - GetCurrentProcessorNumberEx(&procNumber); - } - - if (GCToOSInterface::CanEnableGCNumaAware()) - { - procNumber.Number = (BYTE)gpn; - procNumber.Reserved = 0; - - if (!NumaNodeInfo::GetNumaProcessorNodeEx(&procNumber, node_no)) - { - *node_no = NUMA_NODE_UNDEFINED; - } - } - else - { // no numa setting, each cpu group is treated as a node - *node_no = procNumber.Group; - } -#else // !TARGET_UNIX - *proc_no = procIndex; - if (!GCToOSInterface::CanEnableGCNumaAware() || !NumaNodeInfo::GetNumaProcessorNodeEx(procIndex, (WORD*)node_no)) - { - *node_no = NUMA_NODE_UNDEFINED; - } -#endif // !TARGET_UNIX - } - - return success; -} - -// Parse the confing string describing affinitization ranges and update the passed in affinitySet accordingly -// Parameters: -// config_string - string describing the affinitization range, platform specific -// start_index - the range start index extracted from the config_string -// end_index - the range end index extracted from the config_string, equal to the start_index if only an index and not a range was passed in -// Return: -// true if the configString was successfully parsed, false if it was not correct -bool GCToOSInterface::ParseGCHeapAffinitizeRangesEntry(const char** config_string, size_t* start_index, size_t* end_index) -{ - size_t index_offset = 0; - - char* number_end; -#ifndef TARGET_UNIX - size_t group_number = strtoul(*config_string, &number_end, 10); - - if ((number_end == *config_string) || (*number_end != ':')) - { - // No number or no colon after the number found, invalid format - return false; - } - - // If the user passes in 0 as the CPU group and they don't have > 64 cores, - // honor the affinitized range passed in by bypassing the check. - bool bypass_cpu_range_check = !CanEnableGCCPUGroups() && group_number == 0; - - WORD group_begin; - WORD group_size; - if (!CPUGroupInfo::GetCPUGroupRange((WORD)group_number, &group_begin, &group_size)) - { - if (!bypass_cpu_range_check) - { - // group number out of range - return false; - } - else - { - // the offset in this case where we bypass this check should be from 0 till the # of Processors. - group_begin = 0; - group_size = (WORD)GetTotalProcessorCount(); - } - } - - index_offset = group_begin; - *config_string = number_end + 1; -#endif // !TARGET_UNIX - - size_t start, end; - if (!ParseIndexOrRange(config_string, &start, &end)) - { - return false; - } - -#ifndef TARGET_UNIX - if ((start >= group_size) || (end >= group_size)) - { - // Invalid CPU index values or range - return false; - } -#endif // !TARGET_UNIX - - *start_index = index_offset + start; - *end_index = index_offset + end; - - return true; -} - -// Initialize the critical section -bool CLRCriticalSection::Initialize() -{ - WRAPPER_NO_CONTRACT; - InitializeCriticalSection(&m_cs); - - return true; -} - -// Destroy the critical section -void CLRCriticalSection::Destroy() -{ - WRAPPER_NO_CONTRACT; - DeleteCriticalSection(&m_cs); -} - -// Enter the critical section. Blocks until the section can be entered. -void CLRCriticalSection::Enter() -{ - WRAPPER_NO_CONTRACT; - EnterCriticalSection(&m_cs); -} - -// Leave the critical section -void CLRCriticalSection::Leave() -{ - WRAPPER_NO_CONTRACT; - LeaveCriticalSection(&m_cs); -} - -// An implementation of GCEvent that delegates to -// a CLREvent, which in turn delegates to the PAL. This event -// is also host-aware. -class GCEvent::Impl -{ -private: - CLREvent m_event; - -public: - Impl() = default; - - bool IsValid() - { - WRAPPER_NO_CONTRACT; - - return !!m_event.IsValid(); - } - - void CloseEvent() - { - WRAPPER_NO_CONTRACT; - - _ASSERTE(m_event.IsValid()); - m_event.CloseEvent(); - } - - void Set() - { - WRAPPER_NO_CONTRACT; - - _ASSERTE(m_event.IsValid()); - m_event.Set(); - } - - void Reset() - { - WRAPPER_NO_CONTRACT; - - _ASSERTE(m_event.IsValid()); - m_event.Reset(); - } - - uint32_t Wait(uint32_t timeout, bool alertable) - { - WRAPPER_NO_CONTRACT; - - _ASSERTE(m_event.IsValid()); - return m_event.Wait(timeout, alertable); - } - - bool CreateAutoEvent(bool initialState) - { - CONTRACTL { - NOTHROW; - GC_NOTRIGGER; - } CONTRACTL_END; - - return !!m_event.CreateAutoEventNoThrow(initialState); - } - - bool CreateManualEvent(bool initialState) - { - CONTRACTL { - NOTHROW; - GC_NOTRIGGER; - } CONTRACTL_END; - - return !!m_event.CreateManualEventNoThrow(initialState); - } - - bool CreateOSAutoEvent(bool initialState) - { - CONTRACTL { - NOTHROW; - GC_NOTRIGGER; - } CONTRACTL_END; - - return !!m_event.CreateOSAutoEventNoThrow(initialState); - } - - bool CreateOSManualEvent(bool initialState) - { - CONTRACTL { - NOTHROW; - GC_NOTRIGGER; - } CONTRACTL_END; - - return !!m_event.CreateOSManualEventNoThrow(initialState); - } -}; - -GCEvent::GCEvent() - : m_impl(nullptr) -{ -} - -void GCEvent::CloseEvent() -{ - WRAPPER_NO_CONTRACT; - - _ASSERTE(m_impl != nullptr); - m_impl->CloseEvent(); -} - -void GCEvent::Set() -{ - WRAPPER_NO_CONTRACT; - - _ASSERTE(m_impl != nullptr); - m_impl->Set(); -} - -void GCEvent::Reset() -{ - WRAPPER_NO_CONTRACT; - - _ASSERTE(m_impl != nullptr); - m_impl->Reset(); -} - -uint32_t GCEvent::Wait(uint32_t timeout, bool alertable) -{ - WRAPPER_NO_CONTRACT; - - _ASSERTE(m_impl != nullptr); - return m_impl->Wait(timeout, alertable); -} - -bool GCEvent::CreateManualEventNoThrow(bool initialState) -{ - CONTRACTL { - NOTHROW; - GC_NOTRIGGER; - } CONTRACTL_END; - - _ASSERTE(m_impl == nullptr); - NewHolder event = new (nothrow) GCEvent::Impl(); - if (!event) - { - return false; - } - - event->CreateManualEvent(initialState); - m_impl = event.Extract(); - return true; -} - -bool GCEvent::CreateAutoEventNoThrow(bool initialState) -{ - CONTRACTL { - NOTHROW; - GC_NOTRIGGER; - } CONTRACTL_END; - - _ASSERTE(m_impl == nullptr); - NewHolder event = new (nothrow) GCEvent::Impl(); - if (!event) - { - return false; - } - - event->CreateAutoEvent(initialState); - m_impl = event.Extract(); - return IsValid(); -} - -bool GCEvent::CreateOSAutoEventNoThrow(bool initialState) -{ - CONTRACTL { - NOTHROW; - GC_NOTRIGGER; - } CONTRACTL_END; - - _ASSERTE(m_impl == nullptr); - NewHolder event = new (nothrow) GCEvent::Impl(); - if (!event) - { - return false; - } - - event->CreateOSAutoEvent(initialState); - m_impl = event.Extract(); - return IsValid(); -} - -bool GCEvent::CreateOSManualEventNoThrow(bool initialState) -{ - CONTRACTL { - NOTHROW; - GC_NOTRIGGER; - } CONTRACTL_END; - - _ASSERTE(m_impl == nullptr); - NewHolder event = new (nothrow) GCEvent::Impl(); - if (!event) - { - return false; - } - - event->CreateOSManualEvent(initialState); - m_impl = event.Extract(); - return IsValid(); -} - From 4558252a315b9ec967953ca8d44a8a4f5e5f7cd6 Mon Sep 17 00:00:00 2001 From: Manish Godse <61718172+mangod9@users.noreply.github.com> Date: Tue, 25 Oct 2022 22:51:23 -0700 Subject: [PATCH 2/2] CR feedback --- src/coreclr/gc/windows/CMakeLists.txt | 2 +- src/coreclr/inc/clrconfigvalues.h | 1 - src/coreclr/vm/CMakeLists.txt | 6 ------ 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/coreclr/gc/windows/CMakeLists.txt b/src/coreclr/gc/windows/CMakeLists.txt index 3ac760254b454b..d930761a5e37f4 100644 --- a/src/coreclr/gc/windows/CMakeLists.txt +++ b/src/coreclr/gc/windows/CMakeLists.txt @@ -6,4 +6,4 @@ set(GC_PAL_SOURCES gcenv.windows.cpp ) -add_library(gc_pal STATIC ${GC_PAL_SOURCES} Native.rc) +add_library(gc_pal STATIC ${GC_PAL_SOURCES}) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 81cd74e76250a0..230cd50fd6e8a1 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -272,7 +272,6 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_gcAllowVeryLargeObjects, W("gcAllowVeryLargeOb RETAIL_CONFIG_DWORD_INFO(EXTERNAL_GCStress, W("GCStress"), 0, "Trigger GCs at regular intervals") CONFIG_DWORD_INFO(INTERNAL_GcStressOnDirectCalls, W("GcStressOnDirectCalls"), 0, "Whether to trigger a GC on direct calls") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_HeapVerify, W("HeapVerify"), 0, "When set verifies the integrity of the managed heap on entry and exit of each GC") -RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_GCNumaAware, W("GCNumaAware"), 1, "Specifies if to enable GC NUMA aware") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_GCCpuGroup, W("GCCpuGroup"), 0, "Specifies if to enable GC to support CPU groups") RETAIL_CONFIG_STRING_INFO(EXTERNAL_GCName, W("GCName"), "") /** diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt index 5039e61d54db85..68169ccb4edea2 100644 --- a/src/coreclr/vm/CMakeLists.txt +++ b/src/coreclr/vm/CMakeLists.txt @@ -503,12 +503,6 @@ if (CLR_CMAKE_TARGET_ARCH_AMD64 AND CLR_CMAKE_TARGET_WIN32) ) endif (CLR_CMAKE_TARGET_ARCH_AMD64 AND CLR_CMAKE_TARGET_WIN32) -if (CLR_CMAKE_TARGET_WIN32) - set ( GC_SOURCES_WKS - ${GC_SOURCES_WKS} - ) -endif (CLR_CMAKE_TARGET_WIN32) - set(GC_HEADERS_WKS ${GC_HEADERS_DAC_AND_WKS_COMMON} ../gc/gceventstatus.h