1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
8 * GCToOSInterface implementation
25 #include "../gc/env/gcenv.os.h"
27 #define MAX_PTR ((uint8_t*)(~(ptrdiff_t)0))
30 uint32_t g_pageSizeUnixInl = 0;
33 static AffinitySet g_processAffinitySet;
41 static const uint16_t NoGroup = 0x3ff;
43 GroupProcNo(uint16_t groupProc) : m_groupProc(groupProc)
47 GroupProcNo(uint16_t group, uint16_t procIndex) : m_groupProc((group << 6) | procIndex)
49 assert(group <= 0x3ff);
50 assert(procIndex <= 0x3f);
53 uint16_t GetGroup() { return m_groupProc >> 6; }
54 uint16_t GetProcIndex() { return m_groupProc & 0x3f; }
55 uint16_t GetCombinedValue() { return m_groupProc; }
58 #if !defined(FEATURE_PAL)
60 static bool g_SeLockMemoryPrivilegeAcquired = false;
62 bool InitLargePagesPrivilege()
66 if (!LookupPrivilegeValueW(nullptr, SE_LOCK_MEMORY_NAME, &luid))
71 tp.PrivilegeCount = 1;
72 tp.Privileges[0].Luid = luid;
73 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
76 if (!OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token))
81 BOOL retVal = AdjustTokenPrivileges(token, FALSE, &tp, 0, nullptr, 0);
82 DWORD gls = GetLastError();
100 // Initialize the interface implementation
102 // true if it has succeeded, false if it has failed
103 bool GCToOSInterface::Initialize()
105 LIMITED_METHOD_CONTRACT;
108 g_pageSizeUnixInl = GetOsPageSize();
111 if (CPUGroupInfo::CanEnableGCCPUGroups())
113 // When CPU groups are enabled, then the process is not bound by the process affinity set at process launch.
114 // Set the initial affinity mask so that all processors are enabled.
115 for (size_t i = 0; i < CPUGroupInfo::GetNumActiveProcessors(); i++)
117 g_processAffinitySet.Add(i);
122 // When CPU groups are disabled, the process affinity mask specified at the process launch cannot be
124 uintptr_t pmask, smask;
125 if (!!::GetProcessAffinityMask(::GetCurrentProcess(), (PDWORD_PTR)&pmask, (PDWORD_PTR)&smask))
129 for (size_t i = 0; i < 8 * sizeof(uintptr_t); i++)
131 if ((pmask & ((uintptr_t)1 << i)) != 0)
133 g_processAffinitySet.Add(i);
142 // Shutdown the interface implementation
143 void GCToOSInterface::Shutdown()
145 LIMITED_METHOD_CONTRACT;
148 // Get numeric id of the current thread if possible on the
149 // current platform. It is indended for logging purposes only.
151 // Numeric id of the current thread or 0 if the
152 uint64_t GCToOSInterface::GetCurrentThreadIdForLogging()
154 LIMITED_METHOD_CONTRACT;
155 return ::GetCurrentThreadId();
158 // Get id of the process
160 // Id of the current process
161 uint32_t GCToOSInterface::GetCurrentProcessId()
163 LIMITED_METHOD_CONTRACT;
164 return ::GetCurrentProcessId();
167 // Set ideal processor for the current thread
169 // srcProcNo - processor number the thread currently runs on
170 // dstProcNo - processor number the thread should be migrated to
172 // true if it has succeeded, false if it has failed
173 bool GCToOSInterface::SetCurrentThreadIdealAffinity(uint16_t srcProcNo, uint16_t dstProcNo)
175 LIMITED_METHOD_CONTRACT;
179 GroupProcNo srcGroupProcNo(srcProcNo);
180 GroupProcNo dstGroupProcNo(dstProcNo);
182 if (CPUGroupInfo::CanEnableGCCPUGroups())
184 if (srcGroupProcNo.GetGroup() != dstGroupProcNo.GetGroup())
186 //only set ideal processor when srcProcNo and dstProcNo are in the same cpu
187 //group. DO NOT MOVE THREADS ACROSS CPU GROUPS
192 #if !defined(FEATURE_CORESYSTEM)
193 SetThreadIdealProcessor(GetCurrentThread(), (DWORD)dstGroupProcNo.GetProcIndex());
195 PROCESSOR_NUMBER proc;
197 if (dstGroupProcNo.GetGroup() != GroupProcNo::NoGroup)
199 proc.Group = (WORD)dstGroupProcNo.GetGroup();
200 proc.Number = (BYTE)dstGroupProcNo.GetProcIndex();
203 success = !!SetThreadIdealProcessorEx(GetCurrentThread(), &proc, NULL);
205 #if !defined(FEATURE_PAL)
208 if (GetThreadIdealProcessorEx(GetCurrentThread(), &proc))
210 proc.Number = (BYTE)dstGroupProcNo.GetProcIndex();
211 success = !!SetThreadIdealProcessorEx(GetCurrentThread(), &proc, &proc);
214 #endif // !defined(FEATURE_PAL)
220 // Get the number of the current processor
221 uint32_t GCToOSInterface::GetCurrentProcessorNumber()
223 LIMITED_METHOD_CONTRACT;
225 _ASSERTE(CanGetCurrentProcessorNumber());
226 return ::GetCurrentProcessorNumber();
229 // Check if the OS supports getting current processor number
230 bool GCToOSInterface::CanGetCurrentProcessorNumber()
232 LIMITED_METHOD_CONTRACT;
235 return PAL_HasGetCurrentProcessorNumber();
237 // on all Windows platforms we support this API exists
242 // Flush write buffers of processors that are executing threads of the current process
243 void GCToOSInterface::FlushProcessWriteBuffers()
245 LIMITED_METHOD_CONTRACT;
246 ::FlushProcessWriteBuffers();
249 // Break into a debugger
250 void GCToOSInterface::DebugBreak()
252 LIMITED_METHOD_CONTRACT;
256 // Causes the calling thread to sleep for the specified number of milliseconds
258 // sleepMSec - time to sleep before switching to another thread
259 void GCToOSInterface::Sleep(uint32_t sleepMSec)
261 LIMITED_METHOD_CONTRACT;
262 __SwitchToThread(sleepMSec, 0);
265 // Causes the calling thread to yield execution to another thread that is ready to run on the current processor.
267 // switchCount - number of times the YieldThread was called in a loop
268 void GCToOSInterface::YieldThread(uint32_t switchCount)
270 LIMITED_METHOD_CONTRACT;
271 __SwitchToThread(0, switchCount);
274 // Reserve virtual memory range.
276 // address - starting virtual address, it can be NULL to let the function choose the starting address
277 // size - size of the virtual memory range
278 // alignment - requested memory alignment
279 // flags - flags to control special settings like write watching
281 // Starting virtual address of the reserved range
282 void* GCToOSInterface::VirtualReserve(size_t size, size_t alignment, uint32_t flags)
284 LIMITED_METHOD_CONTRACT;
286 DWORD memFlags = (flags & VirtualReserveFlags::WriteWatch) ? (MEM_RESERVE | MEM_WRITE_WATCH) : MEM_RESERVE;
288 // This is not strictly necessary for a correctness standpoint. Windows already guarantees
289 // allocation granularity alignment when using MEM_RESERVE, so aligning the size here has no effect.
290 // However, ClrVirtualAlloc does expect the size to be aligned to the allocation granularity.
291 size_t aligned_size = (size + g_SystemInfo.dwAllocationGranularity - 1) & ~static_cast<size_t>(g_SystemInfo.dwAllocationGranularity - 1);
294 return ::ClrVirtualAlloc(0, aligned_size, memFlags, PAGE_READWRITE);
298 return ::ClrVirtualAllocAligned(0, aligned_size, memFlags, PAGE_READWRITE, alignment);
302 // Release virtual memory range previously reserved using VirtualReserve
304 // address - starting virtual address
305 // size - size of the virtual memory range
307 // true if it has succeeded, false if it has failed
308 bool GCToOSInterface::VirtualRelease(void* address, size_t size)
310 LIMITED_METHOD_CONTRACT;
312 UNREFERENCED_PARAMETER(size);
313 return !!::ClrVirtualFree(address, 0, MEM_RELEASE);
316 // Commit virtual memory range.
318 // size - size of the virtual memory range
320 // Starting virtual address of the committed range
321 void* GCToOSInterface::VirtualReserveAndCommitLargePages(size_t size)
323 LIMITED_METHOD_CONTRACT;
325 #if !defined(FEATURE_PAL)
326 if (!g_SeLockMemoryPrivilegeAcquired)
328 if (!InitLargePagesPrivilege())
333 g_SeLockMemoryPrivilegeAcquired = true;
336 SIZE_T largePageMinimum = GetLargePageMinimum();
337 size = (size + (largePageMinimum - 1)) & ~(largePageMinimum - 1);
340 return ::ClrVirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE);
343 // Commit virtual memory range. It must be part of a range reserved using VirtualReserve.
345 // address - starting virtual address
346 // size - size of the virtual memory range
348 // true if it has succeeded, false if it has failed
349 bool GCToOSInterface::VirtualCommit(void* address, size_t size, uint16_t node)
351 LIMITED_METHOD_CONTRACT;
353 if (node == NUMA_NODE_UNDEFINED)
355 return ::ClrVirtualAlloc(address, size, MEM_COMMIT, PAGE_READWRITE) != NULL;
359 return NumaNodeInfo::VirtualAllocExNuma(::GetCurrentProcess(), address, size, MEM_COMMIT, PAGE_READWRITE, node) != NULL;
363 // Decomit virtual memory range.
365 // address - starting virtual address
366 // size - size of the virtual memory range
368 // true if it has succeeded, false if it has failed
369 bool GCToOSInterface::VirtualDecommit(void* address, size_t size)
371 LIMITED_METHOD_CONTRACT;
373 return !!::ClrVirtualFree(address, size, MEM_DECOMMIT);
376 // Reset virtual memory range. Indicates that data in the memory range specified by address and size is no
377 // longer of interest, but it should not be decommitted.
379 // address - starting virtual address
380 // size - size of the virtual memory range
381 // unlock - true if the memory range should also be unlocked
383 // true if it has succeeded, false if it has failed
384 bool GCToOSInterface::VirtualReset(void * address, size_t size, bool unlock)
386 LIMITED_METHOD_CONTRACT;
388 bool success = ::ClrVirtualAlloc(address, size, MEM_RESET, PAGE_READWRITE) != NULL;
390 if (success && unlock)
392 // Remove the page range from the working set
393 ::VirtualUnlock(address, size);
395 #endif // FEATURE_PAL
400 // Check if the OS supports write watching
401 bool GCToOSInterface::SupportsWriteWatch()
403 LIMITED_METHOD_CONTRACT;
405 bool writeWatchSupported = false;
407 // check if the OS supports write-watch.
408 // Drawbridge does not support write-watch so we still need to do the runtime detection for them.
409 // Otherwise, all currently supported OSes do support write-watch.
410 void* mem = VirtualReserve (g_SystemInfo.dwAllocationGranularity, 0, VirtualReserveFlags::WriteWatch);
413 VirtualRelease (mem, g_SystemInfo.dwAllocationGranularity);
414 writeWatchSupported = true;
417 return writeWatchSupported;
420 // Reset the write tracking state for the specified virtual memory range.
422 // address - starting virtual address
423 // size - size of the virtual memory range
424 void GCToOSInterface::ResetWriteWatch(void* address, size_t size)
426 LIMITED_METHOD_CONTRACT;
428 ::ResetWriteWatch(address, size);
431 // Retrieve addresses of the pages that are written to in a region of virtual memory
433 // resetState - true indicates to reset the write tracking state
434 // address - starting virtual address
435 // size - size of the virtual memory range
436 // pageAddresses - buffer that receives an array of page addresses in the memory region
437 // pageAddressesCount - on input, size of the lpAddresses array, in array elements
438 // on output, the number of page addresses that are returned in the array.
440 // true if it has succeeded, false if it has failed
441 bool GCToOSInterface::GetWriteWatch(bool resetState, void* address, size_t size, void** pageAddresses, uintptr_t* pageAddressesCount)
443 LIMITED_METHOD_CONTRACT;
445 uint32_t flags = resetState ? 1 : 0;
448 bool success = ::GetWriteWatch(flags, address, size, pageAddresses, (ULONG_PTR*)pageAddressesCount, &granularity) == 0;
449 _ASSERTE (granularity == GetOsPageSize());
454 // Get size of the largest cache on the processor die
456 // trueSize - true to return true cache size, false to return scaled up size based on
457 // the processor architecture
460 size_t GCToOSInterface::GetCacheSizePerLogicalCpu(bool trueSize)
462 LIMITED_METHOD_CONTRACT;
464 return ::GetCacheSizePerLogicalCpu(trueSize);
467 // Sets the calling thread's affinity to only run on the processor specified
469 // procNo - The requested processor for the calling thread.
471 // true if setting the affinity was successful, false otherwise.
472 bool GCToOSInterface::SetThreadAffinity(uint16_t procNo)
474 LIMITED_METHOD_CONTRACT;
476 GroupProcNo groupProcNo(procNo);
478 if (groupProcNo.GetGroup() != GroupProcNo::NoGroup)
481 ga.Group = (WORD)groupProcNo.GetGroup();
482 ga.Reserved[0] = 0; // reserve must be filled with zero
483 ga.Reserved[1] = 0; // otherwise call may fail
485 ga.Mask = (size_t)1 << groupProcNo.GetProcIndex();
486 return !!SetThreadGroupAffinity(GetCurrentThread(), &ga, nullptr);
490 return !!SetThreadAffinityMask(GetCurrentThread(), (DWORD_PTR)1 << groupProcNo.GetProcIndex());
494 // Boosts the calling thread's thread priority to a level higher than the default
499 // true if the priority boost was successful, false otherwise.
500 bool GCToOSInterface::BoostThreadPriority()
502 return !!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
505 // Set the set of processors enabled for GC threads for the current process based on config specified affinity mask and set
507 // configAffinityMask - mask specified by the GCHeapAffinitizeMask config
508 // configAffinitySet - affinity set specified by the GCHeapAffinitizeRanges config
510 // set of enabled processors
511 const AffinitySet* GCToOSInterface::SetGCThreadsAffinitySet(uintptr_t configAffinityMask, const AffinitySet* configAffinitySet)
513 if (CPUGroupInfo::CanEnableGCCPUGroups())
515 if (!configAffinitySet->IsEmpty())
517 // Update the process affinity set using the configured set
518 for (size_t i = 0; i < MAX_SUPPORTED_CPUS; i++)
520 if (g_processAffinitySet.Contains(i) && !configAffinitySet->Contains(i))
522 g_processAffinitySet.Remove(i);
529 if (configAffinityMask != 0)
531 // Update the process affinity set using the configured mask
532 for (size_t i = 0; i < 8 * sizeof(uintptr_t); i++)
534 if (g_processAffinitySet.Contains(i) && ((configAffinityMask & ((uintptr_t)1 << i)) == 0))
536 g_processAffinitySet.Remove(i);
542 return &g_processAffinitySet;
545 // Get number of processors assigned to the current process
547 // The number of processors
548 uint32_t GCToOSInterface::GetCurrentProcessCpuCount()
550 LIMITED_METHOD_CONTRACT;
552 // GetCurrentProcessCpuCount only returns up to 64 procs.
553 return CPUGroupInfo::CanEnableGCCPUGroups() ?
554 GCToOSInterface::GetTotalProcessorCount():
555 ::GetCurrentProcessCpuCount();
558 // Return the size of the user-mode portion of the virtual address space of this process.
560 // non zero if it has succeeded, 0 if it has failed
561 size_t GCToOSInterface::GetVirtualMemoryLimit()
563 LIMITED_METHOD_CONTRACT;
565 MEMORYSTATUSEX memStatus;
566 ::GetProcessMemoryLoad(&memStatus);
568 return (size_t)memStatus.ullTotalVirtual;
571 static size_t g_RestrictedPhysicalMemoryLimit = (size_t)MAX_PTR;
575 // For 32-bit processes the virtual address range could be smaller than the amount of physical
576 // memory on the machine/in the container, we need to restrict by the VM.
577 static bool g_UseRestrictedVirtualMemory = false;
579 typedef BOOL (WINAPI *PGET_PROCESS_MEMORY_INFO)(HANDLE handle, PROCESS_MEMORY_COUNTERS* memCounters, uint32_t cb);
580 static PGET_PROCESS_MEMORY_INFO GCGetProcessMemoryInfo = 0;
582 typedef BOOL (WINAPI *PIS_PROCESS_IN_JOB)(HANDLE processHandle, HANDLE jobHandle, BOOL* result);
583 typedef BOOL (WINAPI *PQUERY_INFORMATION_JOB_OBJECT)(HANDLE jobHandle, JOBOBJECTINFOCLASS jobObjectInfoClass, void* lpJobObjectInfo, DWORD cbJobObjectInfoLength, LPDWORD lpReturnLength);
585 static size_t GetRestrictedPhysicalMemoryLimit()
587 LIMITED_METHOD_CONTRACT;
589 // The limit was cached already
590 if (g_RestrictedPhysicalMemoryLimit != (size_t)MAX_PTR)
591 return g_RestrictedPhysicalMemoryLimit;
593 size_t job_physical_memory_limit = (size_t)MAX_PTR;
594 uint64_t total_virtual = 0;
595 uint64_t total_physical = 0;
596 BOOL in_job_p = FALSE;
597 HINSTANCE hinstKernel32 = 0;
599 PIS_PROCESS_IN_JOB GCIsProcessInJob = 0;
600 PQUERY_INFORMATION_JOB_OBJECT GCQueryInformationJobObject = 0;
602 GCIsProcessInJob = &(::IsProcessInJob);
604 if (!GCIsProcessInJob(GetCurrentProcess(), NULL, &in_job_p))
609 hinstKernel32 = WszLoadLibrary(L"kernel32.dll");
613 GCGetProcessMemoryInfo = (PGET_PROCESS_MEMORY_INFO)GetProcAddress(hinstKernel32, "K32GetProcessMemoryInfo");
615 if (!GCGetProcessMemoryInfo)
618 GCQueryInformationJobObject = &(::QueryInformationJobObject);
620 if (!GCQueryInformationJobObject)
623 JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info;
624 if (GCQueryInformationJobObject (NULL, JobObjectExtendedLimitInformation, &limit_info,
625 sizeof(limit_info), NULL))
627 size_t job_memory_limit = (size_t)MAX_PTR;
628 size_t job_process_memory_limit = (size_t)MAX_PTR;
629 size_t job_workingset_limit = (size_t)MAX_PTR;
631 // Notes on the NT job object:
633 // You can specific a bigger process commit or working set limit than
634 // job limit which is pointless so we use the smallest of all 3 as
635 // to calculate our "physical memory load" or "available physical memory"
636 // when running inside a job object, ie, we treat this as the amount of physical memory
637 // our process is allowed to use.
639 // The commit limit is already reflected by default when you run in a
640 // job but the physical memory load is not.
642 if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_JOB_MEMORY) != 0)
643 job_memory_limit = limit_info.JobMemoryLimit;
644 if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_PROCESS_MEMORY) != 0)
645 job_process_memory_limit = limit_info.ProcessMemoryLimit;
646 if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_WORKINGSET) != 0)
647 job_workingset_limit = limit_info.BasicLimitInformation.MaximumWorkingSetSize;
649 if ((job_memory_limit != (size_t)MAX_PTR) ||
650 (job_process_memory_limit != (size_t)MAX_PTR) ||
651 (job_workingset_limit != (size_t)MAX_PTR))
653 job_physical_memory_limit = min (job_memory_limit, job_process_memory_limit);
654 job_physical_memory_limit = min (job_physical_memory_limit, job_workingset_limit);
657 ::GetProcessMemoryLoad(&ms);
658 total_virtual = ms.ullTotalVirtual;
659 total_physical = ms.ullAvailPhys;
661 // A sanity check in case someone set a larger limit than there is actual physical memory.
662 job_physical_memory_limit = (size_t) min (job_physical_memory_limit, ms.ullTotalPhys);
668 if (job_physical_memory_limit == (size_t)MAX_PTR)
670 job_physical_memory_limit = 0;
672 if (hinstKernel32 != 0)
674 FreeLibrary(hinstKernel32);
676 GCGetProcessMemoryInfo = 0;
680 // Check to see if we are limited by VM.
681 if (total_virtual == 0)
684 ::GetProcessMemoryLoad(&ms);
686 total_virtual = ms.ullTotalVirtual;
687 total_physical = ms.ullTotalPhys;
690 if (job_physical_memory_limit != 0)
692 total_physical = job_physical_memory_limit;
695 if (total_virtual < total_physical)
697 if (hinstKernel32 != 0)
699 // We can also free the lib here - if we are limited by VM we will not be calling
700 // GetProcessMemoryInfo.
701 FreeLibrary(hinstKernel32);
702 GCGetProcessMemoryInfo = 0;
704 g_UseRestrictedVirtualMemory = true;
705 job_physical_memory_limit = (size_t)total_virtual;
708 VolatileStore(&g_RestrictedPhysicalMemoryLimit, job_physical_memory_limit);
709 return g_RestrictedPhysicalMemoryLimit;
714 static size_t GetRestrictedPhysicalMemoryLimit()
716 LIMITED_METHOD_CONTRACT;
718 // The limit was cached already
719 if (g_RestrictedPhysicalMemoryLimit != (size_t)MAX_PTR)
720 return g_RestrictedPhysicalMemoryLimit;
722 size_t memory_limit = PAL_GetRestrictedPhysicalMemoryLimit();
724 VolatileStore(&g_RestrictedPhysicalMemoryLimit, memory_limit);
725 return g_RestrictedPhysicalMemoryLimit;
727 #endif // FEATURE_PAL
730 // Get the physical memory that this process can use.
732 // non zero if it has succeeded, 0 if it has failed
734 // PERF TODO: Requires more work to not treat the restricted case to be special.
735 // To be removed before 3.0 ships.
736 uint64_t GCToOSInterface::GetPhysicalMemoryLimit(bool* is_restricted)
738 LIMITED_METHOD_CONTRACT;
741 *is_restricted = false;
743 size_t restricted_limit = GetRestrictedPhysicalMemoryLimit();
744 if (restricted_limit != 0)
748 && !g_UseRestrictedVirtualMemory
751 *is_restricted = true;
753 return restricted_limit;
756 MEMORYSTATUSEX memStatus;
757 ::GetProcessMemoryLoad(&memStatus);
759 return memStatus.ullTotalPhys;
764 // memory_load - A number between 0 and 100 that specifies the approximate percentage of physical memory
765 // that is in use (0 indicates no memory use and 100 indicates full memory use).
766 // available_physical - The amount of physical memory currently available, in bytes.
767 // available_page_file - The maximum amount of memory the current process can commit, in bytes.
769 // Any parameter can be null.
770 void GCToOSInterface::GetMemoryStatus(uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file)
772 LIMITED_METHOD_CONTRACT;
774 uint64_t restricted_limit = GetRestrictedPhysicalMemoryLimit();
775 if (restricted_limit != 0)
777 size_t workingSetSize;
780 if (!g_UseRestrictedVirtualMemory)
782 PROCESS_MEMORY_COUNTERS pmc;
783 status = GCGetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc));
784 workingSetSize = pmc.WorkingSetSize;
787 status = PAL_GetPhysicalMemoryUsed(&workingSetSize);
792 *memory_load = (uint32_t)((float)workingSetSize * 100.0 / (float)restricted_limit);
793 if (available_physical)
795 if(workingSetSize > restricted_limit)
796 *available_physical = 0;
798 *available_physical = restricted_limit - workingSetSize;
800 // Available page file doesn't mean much when physical memory is restricted since
801 // we don't know how much of it is available to this process so we are not going to
802 // bother to make another OS call for it.
803 if (available_page_file)
804 *available_page_file = 0;
811 ::GetProcessMemoryLoad(&ms);
814 if (g_UseRestrictedVirtualMemory)
816 _ASSERTE (ms.ullTotalVirtual == restricted_limit);
817 if (memory_load != NULL)
818 *memory_load = (uint32_t)((float)(ms.ullTotalVirtual - ms.ullAvailVirtual) * 100.0 / (float)ms.ullTotalVirtual);
819 if (available_physical != NULL)
820 *available_physical = ms.ullTotalVirtual;
822 // Available page file isn't helpful when we are restricted by virtual memory
823 // since the amount of memory we can reserve is less than the amount of
824 // memory we can commit.
825 if (available_page_file != NULL)
826 *available_page_file = 0;
829 #endif //!FEATURE_PAL
831 if (memory_load != NULL)
832 *memory_load = ms.dwMemoryLoad;
833 if (available_physical != NULL)
834 *available_physical = ms.ullAvailPhys;
835 if (available_page_file != NULL)
836 *available_page_file = ms.ullAvailPageFile;
840 // Get a high precision performance counter
843 int64_t GCToOSInterface::QueryPerformanceCounter()
845 LIMITED_METHOD_CONTRACT;
848 if (!::QueryPerformanceCounter(&ts))
851 _ASSERTE(!"Fatal Error - cannot query performance counter.");
852 EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); // TODO: fatal error
858 // Get a frequency of the high precision performance counter
860 // The counter frequency
861 int64_t GCToOSInterface::QueryPerformanceFrequency()
863 LIMITED_METHOD_CONTRACT;
865 LARGE_INTEGER frequency;
866 if (!::QueryPerformanceFrequency(&frequency))
869 _ASSERTE(!"Fatal Error - cannot query performance counter.");
870 EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); // TODO: fatal error
873 return frequency.QuadPart;
876 // Get a time stamp with a low precision
878 // Time stamp in milliseconds
879 uint32_t GCToOSInterface::GetLowPrecisionTimeStamp()
881 LIMITED_METHOD_CONTRACT;
883 return ::GetTickCount();
886 uint32_t GCToOSInterface::GetTotalProcessorCount()
888 LIMITED_METHOD_CONTRACT;
890 if (CPUGroupInfo::CanEnableGCCPUGroups())
892 return CPUGroupInfo::GetNumActiveProcessors();
896 return g_SystemInfo.dwNumberOfProcessors;
900 bool GCToOSInterface::CanEnableGCNumaAware()
902 LIMITED_METHOD_CONTRACT;
904 return NumaNodeInfo::CanEnableGCNumaAware() != FALSE;
907 bool GCToOSInterface::GetNumaProcessorNode(uint16_t proc_no, uint16_t *node_no)
909 LIMITED_METHOD_CONTRACT;
911 GroupProcNo groupProcNo(proc_no);
913 PROCESSOR_NUMBER procNumber;
914 procNumber.Group = groupProcNo.GetGroup();
915 procNumber.Number = (BYTE)groupProcNo.GetProcIndex();
916 procNumber.Reserved = 0;
918 return NumaNodeInfo::GetNumaProcessorNodeEx(&procNumber, node_no) != FALSE;
921 // Get processor number and optionally its NUMA node number for the specified heap number
923 // heap_number - heap number to get the result for
924 // proc_no - set to the selected processor number
925 // node_no - set to the NUMA node of the selected processor or to NUMA_NODE_UNDEFINED
927 // true if it succeeded
928 bool GCToOSInterface::GetProcessorForHeap(uint16_t heap_number, uint16_t* proc_no, uint16_t* node_no)
930 bool success = false;
932 if (CPUGroupInfo::CanEnableGCCPUGroups())
935 CPUGroupInfo::GetGroupForProcessor((uint16_t)heap_number, &gn, &gpn);
937 *proc_no = GroupProcNo(gn, gpn).GetCombinedValue();
938 if (GCToOSInterface::CanEnableGCNumaAware())
940 if (!GCToOSInterface::GetNumaProcessorNode(*proc_no, node_no))
942 *node_no = NUMA_NODE_UNDEFINED;
946 { // no numa setting, each cpu group is treated as a node
955 uint8_t proc_number = 0;
956 for (uintptr_t mask = 1; mask != 0; mask <<= 1)
958 if (g_processAffinitySet.Contains(proc_number))
960 if (bit_number == heap_number)
962 *proc_no = GroupProcNo(GroupProcNo::NoGroup, proc_number).GetCombinedValue();
964 if (GCToOSInterface::CanEnableGCNumaAware())
966 if (!GCToOSInterface::GetNumaProcessorNode(proc_number, node_no))
968 *node_no = NUMA_NODE_UNDEFINED;
984 // Parse the confing string describing affinitization ranges and update the passed in affinitySet accordingly
986 // config_string - string describing the affinitization range, platform specific
987 // start_index - the range start index extracted from the config_string
988 // 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
990 // true if the configString was successfully parsed, false if it was not correct
991 bool GCToOSInterface::ParseGCHeapAffinitizeRangesEntry(const char** config_string, size_t* start_index, size_t* end_index)
993 size_t index_offset = 0;
996 size_t group_number = strtoul(*config_string, &number_end, 10);
998 if ((number_end == *config_string) || (*number_end != ':'))
1000 // No number or no colon after the number found, invalid format
1006 if (!CPUGroupInfo::GetCPUGroupRange((WORD)group_number, &group_begin, &group_size))
1008 // group number out of range
1012 index_offset = group_begin;
1013 *config_string = number_end + 1;
1016 if (!ParseIndexOrRange(config_string, &start, &end))
1021 if ((start >= group_size) || (end >= group_size))
1023 // Invalid CPU index values or range
1027 *start_index = index_offset + start;
1028 *end_index = index_offset + end;
1033 // Initialize the critical section
1034 void CLRCriticalSection::Initialize()
1036 WRAPPER_NO_CONTRACT;
1037 UnsafeInitializeCriticalSection(&m_cs);
1040 // Destroy the critical section
1041 void CLRCriticalSection::Destroy()
1043 WRAPPER_NO_CONTRACT;
1044 UnsafeDeleteCriticalSection(&m_cs);
1047 // Enter the critical section. Blocks until the section can be entered.
1048 void CLRCriticalSection::Enter()
1050 WRAPPER_NO_CONTRACT;
1051 UnsafeEnterCriticalSection(&m_cs);
1054 // Leave the critical section
1055 void CLRCriticalSection::Leave()
1057 WRAPPER_NO_CONTRACT;
1058 UnsafeLeaveCriticalSection(&m_cs);
1061 // An implementatino of GCEvent that delegates to
1062 // a CLREvent, which in turn delegates to the PAL. This event
1063 // is also host-aware.
1074 WRAPPER_NO_CONTRACT;
1076 return !!m_event.IsValid();
1081 WRAPPER_NO_CONTRACT;
1083 assert(m_event.IsValid());
1084 m_event.CloseEvent();
1089 WRAPPER_NO_CONTRACT;
1091 assert(m_event.IsValid());
1097 WRAPPER_NO_CONTRACT;
1099 assert(m_event.IsValid());
1103 uint32_t Wait(uint32_t timeout, bool alertable)
1105 WRAPPER_NO_CONTRACT;
1107 assert(m_event.IsValid());
1108 return m_event.Wait(timeout, alertable);
1111 bool CreateAutoEvent(bool initialState)
1118 return !!m_event.CreateAutoEventNoThrow(initialState);
1121 bool CreateManualEvent(bool initialState)
1128 return !!m_event.CreateManualEventNoThrow(initialState);
1131 bool CreateOSAutoEvent(bool initialState)
1138 return !!m_event.CreateOSAutoEventNoThrow(initialState);
1141 bool CreateOSManualEvent(bool initialState)
1148 return !!m_event.CreateOSManualEventNoThrow(initialState);
1157 void GCEvent::CloseEvent()
1159 WRAPPER_NO_CONTRACT;
1161 assert(m_impl != nullptr);
1162 m_impl->CloseEvent();
1167 WRAPPER_NO_CONTRACT;
1169 assert(m_impl != nullptr);
1173 void GCEvent::Reset()
1175 WRAPPER_NO_CONTRACT;
1177 assert(m_impl != nullptr);
1181 uint32_t GCEvent::Wait(uint32_t timeout, bool alertable)
1183 WRAPPER_NO_CONTRACT;
1185 assert(m_impl != nullptr);
1186 return m_impl->Wait(timeout, alertable);
1189 bool GCEvent::CreateManualEventNoThrow(bool initialState)
1196 assert(m_impl == nullptr);
1197 NewHolder<GCEvent::Impl> event = new (nothrow) GCEvent::Impl();
1203 event->CreateManualEvent(initialState);
1204 m_impl = event.Extract();
1208 bool GCEvent::CreateAutoEventNoThrow(bool initialState)
1215 assert(m_impl == nullptr);
1216 NewHolder<GCEvent::Impl> event = new (nothrow) GCEvent::Impl();
1222 event->CreateAutoEvent(initialState);
1223 m_impl = event.Extract();
1227 bool GCEvent::CreateOSAutoEventNoThrow(bool initialState)
1234 assert(m_impl == nullptr);
1235 NewHolder<GCEvent::Impl> event = new (nothrow) GCEvent::Impl();
1241 event->CreateOSAutoEvent(initialState);
1242 m_impl = event.Extract();
1246 bool GCEvent::CreateOSManualEventNoThrow(bool initialState)
1253 assert(m_impl == nullptr);
1254 NewHolder<GCEvent::Impl> event = new (nothrow) GCEvent::Impl();
1260 event->CreateOSManualEvent(initialState);
1261 m_impl = event.Extract();