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 // Initialize the interface implementation
60 // true if it has succeeded, false if it has failed
61 bool GCToOSInterface::Initialize()
63 LIMITED_METHOD_CONTRACT;
66 g_pageSizeUnixInl = GetOsPageSize();
69 if (CPUGroupInfo::CanEnableGCCPUGroups())
71 // When CPU groups are enabled, then the process is not bound by the process affinity set at process launch.
72 // Set the initial affinity mask so that all processors are enabled.
73 for (size_t i = 0; i < CPUGroupInfo::GetNumActiveProcessors(); i++)
75 g_processAffinitySet.Add(i);
80 // When CPU groups are disabled, the process affinity mask specified at the process launch cannot be
82 uintptr_t pmask, smask;
83 if (!!::GetProcessAffinityMask(::GetCurrentProcess(), (PDWORD_PTR)&pmask, (PDWORD_PTR)&smask))
87 for (size_t i = 0; i < 8 * sizeof(uintptr_t); i++)
89 if ((pmask & ((uintptr_t)1 << i)) != 0)
91 g_processAffinitySet.Add(i);
100 // Shutdown the interface implementation
101 void GCToOSInterface::Shutdown()
103 LIMITED_METHOD_CONTRACT;
106 // Get numeric id of the current thread if possible on the
107 // current platform. It is indended for logging purposes only.
109 // Numeric id of the current thread or 0 if the
110 uint64_t GCToOSInterface::GetCurrentThreadIdForLogging()
112 LIMITED_METHOD_CONTRACT;
113 return ::GetCurrentThreadId();
116 // Get id of the process
118 // Id of the current process
119 uint32_t GCToOSInterface::GetCurrentProcessId()
121 LIMITED_METHOD_CONTRACT;
122 return ::GetCurrentProcessId();
125 // Set ideal processor for the current thread
127 // srcProcNo - processor number the thread currently runs on
128 // dstProcNo - processor number the thread should be migrated to
130 // true if it has succeeded, false if it has failed
131 bool GCToOSInterface::SetCurrentThreadIdealAffinity(uint16_t srcProcNo, uint16_t dstProcNo)
133 LIMITED_METHOD_CONTRACT;
137 GroupProcNo srcGroupProcNo(srcProcNo);
138 GroupProcNo dstGroupProcNo(dstProcNo);
140 if (CPUGroupInfo::CanEnableGCCPUGroups())
142 if (srcGroupProcNo.GetGroup() != dstGroupProcNo.GetGroup())
144 //only set ideal processor when srcProcNo and dstProcNo are in the same cpu
145 //group. DO NOT MOVE THREADS ACROSS CPU GROUPS
150 #if !defined(FEATURE_CORESYSTEM)
151 SetThreadIdealProcessor(GetCurrentThread(), (DWORD)dstGroupProcNo.GetProcIndex());
153 PROCESSOR_NUMBER proc;
155 if (dstGroupProcNo.GetGroup() != GroupProcNo::NoGroup)
157 proc.Group = (WORD)dstGroupProcNo.GetGroup();
158 proc.Number = (BYTE)dstGroupProcNo.GetProcIndex();
161 success = !!SetThreadIdealProcessorEx(GetCurrentThread(), &proc, NULL);
163 #if !defined(FEATURE_PAL)
166 if (GetThreadIdealProcessorEx(GetCurrentThread(), &proc))
168 proc.Number = (BYTE)dstGroupProcNo.GetProcIndex();
169 success = !!SetThreadIdealProcessorEx(GetCurrentThread(), &proc, &proc);
172 #endif // !defined(FEATURE_PAL)
178 // Get the number of the current processor
179 uint32_t GCToOSInterface::GetCurrentProcessorNumber()
181 LIMITED_METHOD_CONTRACT;
183 _ASSERTE(CanGetCurrentProcessorNumber());
184 return ::GetCurrentProcessorNumber();
187 // Check if the OS supports getting current processor number
188 bool GCToOSInterface::CanGetCurrentProcessorNumber()
190 LIMITED_METHOD_CONTRACT;
193 return PAL_HasGetCurrentProcessorNumber();
195 // on all Windows platforms we support this API exists
200 // Flush write buffers of processors that are executing threads of the current process
201 void GCToOSInterface::FlushProcessWriteBuffers()
203 LIMITED_METHOD_CONTRACT;
204 ::FlushProcessWriteBuffers();
207 // Break into a debugger
208 void GCToOSInterface::DebugBreak()
210 LIMITED_METHOD_CONTRACT;
214 // Causes the calling thread to sleep for the specified number of milliseconds
216 // sleepMSec - time to sleep before switching to another thread
217 void GCToOSInterface::Sleep(uint32_t sleepMSec)
219 LIMITED_METHOD_CONTRACT;
220 __SwitchToThread(sleepMSec, 0);
223 // Causes the calling thread to yield execution to another thread that is ready to run on the current processor.
225 // switchCount - number of times the YieldThread was called in a loop
226 void GCToOSInterface::YieldThread(uint32_t switchCount)
228 LIMITED_METHOD_CONTRACT;
229 __SwitchToThread(0, switchCount);
232 // Reserve virtual memory range.
234 // address - starting virtual address, it can be NULL to let the function choose the starting address
235 // size - size of the virtual memory range
236 // alignment - requested memory alignment
237 // flags - flags to control special settings like write watching
239 // Starting virtual address of the reserved range
240 void* GCToOSInterface::VirtualReserve(size_t size, size_t alignment, uint32_t flags)
242 LIMITED_METHOD_CONTRACT;
244 DWORD memFlags = (flags & VirtualReserveFlags::WriteWatch) ? (MEM_RESERVE | MEM_WRITE_WATCH) : MEM_RESERVE;
246 // This is not strictly necessary for a correctness standpoint. Windows already guarantees
247 // allocation granularity alignment when using MEM_RESERVE, so aligning the size here has no effect.
248 // However, ClrVirtualAlloc does expect the size to be aligned to the allocation granularity.
249 size_t aligned_size = (size + g_SystemInfo.dwAllocationGranularity - 1) & ~static_cast<size_t>(g_SystemInfo.dwAllocationGranularity - 1);
252 return ::ClrVirtualAlloc(0, aligned_size, memFlags, PAGE_READWRITE);
256 return ::ClrVirtualAllocAligned(0, aligned_size, memFlags, PAGE_READWRITE, alignment);
260 // Release virtual memory range previously reserved using VirtualReserve
262 // address - starting virtual address
263 // size - size of the virtual memory range
265 // true if it has succeeded, false if it has failed
266 bool GCToOSInterface::VirtualRelease(void* address, size_t size)
268 LIMITED_METHOD_CONTRACT;
270 UNREFERENCED_PARAMETER(size);
271 return !!::ClrVirtualFree(address, 0, MEM_RELEASE);
274 // Commit virtual memory range. It must be part of a range reserved using VirtualReserve.
276 // address - starting virtual address
277 // size - size of the virtual memory range
279 // true if it has succeeded, false if it has failed
280 bool GCToOSInterface::VirtualCommit(void* address, size_t size, uint16_t node)
282 LIMITED_METHOD_CONTRACT;
284 if (node == NUMA_NODE_UNDEFINED)
286 return ::ClrVirtualAlloc(address, size, MEM_COMMIT, PAGE_READWRITE) != NULL;
290 return NumaNodeInfo::VirtualAllocExNuma(::GetCurrentProcess(), address, size, MEM_COMMIT, PAGE_READWRITE, node) != NULL;
294 // Decomit virtual memory range.
296 // address - starting virtual address
297 // size - size of the virtual memory range
299 // true if it has succeeded, false if it has failed
300 bool GCToOSInterface::VirtualDecommit(void* address, size_t size)
302 LIMITED_METHOD_CONTRACT;
304 return !!::ClrVirtualFree(address, size, MEM_DECOMMIT);
307 // Reset virtual memory range. Indicates that data in the memory range specified by address and size is no
308 // longer of interest, but it should not be decommitted.
310 // address - starting virtual address
311 // size - size of the virtual memory range
312 // unlock - true if the memory range should also be unlocked
314 // true if it has succeeded, false if it has failed
315 bool GCToOSInterface::VirtualReset(void * address, size_t size, bool unlock)
317 LIMITED_METHOD_CONTRACT;
319 bool success = ::ClrVirtualAlloc(address, size, MEM_RESET, PAGE_READWRITE) != NULL;
321 if (success && unlock)
323 // Remove the page range from the working set
324 ::VirtualUnlock(address, size);
326 #endif // FEATURE_PAL
331 // Check if the OS supports write watching
332 bool GCToOSInterface::SupportsWriteWatch()
334 LIMITED_METHOD_CONTRACT;
336 bool writeWatchSupported = false;
338 // check if the OS supports write-watch.
339 // Drawbridge does not support write-watch so we still need to do the runtime detection for them.
340 // Otherwise, all currently supported OSes do support write-watch.
341 void* mem = VirtualReserve (g_SystemInfo.dwAllocationGranularity, 0, VirtualReserveFlags::WriteWatch);
344 VirtualRelease (mem, g_SystemInfo.dwAllocationGranularity);
345 writeWatchSupported = true;
348 return writeWatchSupported;
351 // Reset the write tracking state for the specified virtual memory range.
353 // address - starting virtual address
354 // size - size of the virtual memory range
355 void GCToOSInterface::ResetWriteWatch(void* address, size_t size)
357 LIMITED_METHOD_CONTRACT;
359 ::ResetWriteWatch(address, size);
362 // Retrieve addresses of the pages that are written to in a region of virtual memory
364 // resetState - true indicates to reset the write tracking state
365 // address - starting virtual address
366 // size - size of the virtual memory range
367 // pageAddresses - buffer that receives an array of page addresses in the memory region
368 // pageAddressesCount - on input, size of the lpAddresses array, in array elements
369 // on output, the number of page addresses that are returned in the array.
371 // true if it has succeeded, false if it has failed
372 bool GCToOSInterface::GetWriteWatch(bool resetState, void* address, size_t size, void** pageAddresses, uintptr_t* pageAddressesCount)
374 LIMITED_METHOD_CONTRACT;
376 uint32_t flags = resetState ? 1 : 0;
379 bool success = ::GetWriteWatch(flags, address, size, pageAddresses, (ULONG_PTR*)pageAddressesCount, &granularity) == 0;
380 _ASSERTE (granularity == GetOsPageSize());
385 // Get size of the largest cache on the processor die
387 // trueSize - true to return true cache size, false to return scaled up size based on
388 // the processor architecture
391 size_t GCToOSInterface::GetCacheSizePerLogicalCpu(bool trueSize)
393 LIMITED_METHOD_CONTRACT;
395 return ::GetCacheSizePerLogicalCpu(trueSize);
398 // Sets the calling thread's affinity to only run on the processor specified
400 // procNo - The requested processor for the calling thread.
402 // true if setting the affinity was successful, false otherwise.
403 bool GCToOSInterface::SetThreadAffinity(uint16_t procNo)
405 LIMITED_METHOD_CONTRACT;
407 GroupProcNo groupProcNo(procNo);
409 if (groupProcNo.GetGroup() != GroupProcNo::NoGroup)
412 ga.Group = (WORD)groupProcNo.GetGroup();
413 ga.Reserved[0] = 0; // reserve must be filled with zero
414 ga.Reserved[1] = 0; // otherwise call may fail
416 ga.Mask = (size_t)1 << groupProcNo.GetProcIndex();
417 return !!SetThreadGroupAffinity(GetCurrentThread(), &ga, nullptr);
421 return !!SetThreadAffinityMask(GetCurrentThread(), (DWORD_PTR)1 << groupProcNo.GetProcIndex());
425 // Boosts the calling thread's thread priority to a level higher than the default
430 // true if the priority boost was successful, false otherwise.
431 bool GCToOSInterface::BoostThreadPriority()
433 return !!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
436 // Set the set of processors enabled for GC threads for the current process based on config specified affinity mask and set
438 // configAffinityMask - mask specified by the GCHeapAffinitizeMask config
439 // configAffinitySet - affinity set specified by the GCHeapAffinitizeRanges config
441 // set of enabled processors
442 const AffinitySet* GCToOSInterface::SetGCThreadsAffinitySet(uintptr_t configAffinityMask, const AffinitySet* configAffinitySet)
444 if (CPUGroupInfo::CanEnableGCCPUGroups())
446 if (!configAffinitySet->IsEmpty())
448 // Update the process affinity set using the configured set
449 for (size_t i = 0; i < MAX_SUPPORTED_CPUS; i++)
451 if (g_processAffinitySet.Contains(i) && !configAffinitySet->Contains(i))
453 g_processAffinitySet.Remove(i);
460 if (configAffinityMask != 0)
462 // Update the process affinity set using the configured mask
463 for (size_t i = 0; i < 8 * sizeof(uintptr_t); i++)
465 if (g_processAffinitySet.Contains(i) && ((configAffinityMask & ((uintptr_t)1 << i)) == 0))
467 g_processAffinitySet.Remove(i);
473 return &g_processAffinitySet;
476 // Get number of processors assigned to the current process
478 // The number of processors
479 uint32_t GCToOSInterface::GetCurrentProcessCpuCount()
481 LIMITED_METHOD_CONTRACT;
483 // GetCurrentProcessCpuCount only returns up to 64 procs.
484 return CPUGroupInfo::CanEnableGCCPUGroups() ?
485 GCToOSInterface::GetTotalProcessorCount():
486 ::GetCurrentProcessCpuCount();
489 // Return the size of the user-mode portion of the virtual address space of this process.
491 // non zero if it has succeeded, 0 if it has failed
492 size_t GCToOSInterface::GetVirtualMemoryLimit()
494 LIMITED_METHOD_CONTRACT;
496 MEMORYSTATUSEX memStatus;
497 ::GetProcessMemoryLoad(&memStatus);
499 return (size_t)memStatus.ullTotalVirtual;
502 static size_t g_RestrictedPhysicalMemoryLimit = (size_t)MAX_PTR;
506 // For 32-bit processes the virtual address range could be smaller than the amount of physical
507 // memory on the machine/in the container, we need to restrict by the VM.
508 static bool g_UseRestrictedVirtualMemory = false;
510 typedef BOOL (WINAPI *PGET_PROCESS_MEMORY_INFO)(HANDLE handle, PROCESS_MEMORY_COUNTERS* memCounters, uint32_t cb);
511 static PGET_PROCESS_MEMORY_INFO GCGetProcessMemoryInfo = 0;
513 typedef BOOL (WINAPI *PIS_PROCESS_IN_JOB)(HANDLE processHandle, HANDLE jobHandle, BOOL* result);
514 typedef BOOL (WINAPI *PQUERY_INFORMATION_JOB_OBJECT)(HANDLE jobHandle, JOBOBJECTINFOCLASS jobObjectInfoClass, void* lpJobObjectInfo, DWORD cbJobObjectInfoLength, LPDWORD lpReturnLength);
516 static size_t GetRestrictedPhysicalMemoryLimit()
518 LIMITED_METHOD_CONTRACT;
520 // The limit was cached already
521 if (g_RestrictedPhysicalMemoryLimit != (size_t)MAX_PTR)
522 return g_RestrictedPhysicalMemoryLimit;
524 size_t job_physical_memory_limit = (size_t)MAX_PTR;
525 uint64_t total_virtual = 0;
526 uint64_t total_physical = 0;
527 BOOL in_job_p = FALSE;
528 HINSTANCE hinstKernel32 = 0;
530 PIS_PROCESS_IN_JOB GCIsProcessInJob = 0;
531 PQUERY_INFORMATION_JOB_OBJECT GCQueryInformationJobObject = 0;
533 GCIsProcessInJob = &(::IsProcessInJob);
535 if (!GCIsProcessInJob(GetCurrentProcess(), NULL, &in_job_p))
540 hinstKernel32 = WszLoadLibrary(L"kernel32.dll");
544 GCGetProcessMemoryInfo = (PGET_PROCESS_MEMORY_INFO)GetProcAddress(hinstKernel32, "K32GetProcessMemoryInfo");
546 if (!GCGetProcessMemoryInfo)
549 GCQueryInformationJobObject = &(::QueryInformationJobObject);
551 if (!GCQueryInformationJobObject)
554 JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info;
555 if (GCQueryInformationJobObject (NULL, JobObjectExtendedLimitInformation, &limit_info,
556 sizeof(limit_info), NULL))
558 size_t job_memory_limit = (size_t)MAX_PTR;
559 size_t job_process_memory_limit = (size_t)MAX_PTR;
560 size_t job_workingset_limit = (size_t)MAX_PTR;
562 // Notes on the NT job object:
564 // You can specific a bigger process commit or working set limit than
565 // job limit which is pointless so we use the smallest of all 3 as
566 // to calculate our "physical memory load" or "available physical memory"
567 // when running inside a job object, ie, we treat this as the amount of physical memory
568 // our process is allowed to use.
570 // The commit limit is already reflected by default when you run in a
571 // job but the physical memory load is not.
573 if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_JOB_MEMORY) != 0)
574 job_memory_limit = limit_info.JobMemoryLimit;
575 if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_PROCESS_MEMORY) != 0)
576 job_process_memory_limit = limit_info.ProcessMemoryLimit;
577 if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_WORKINGSET) != 0)
578 job_workingset_limit = limit_info.BasicLimitInformation.MaximumWorkingSetSize;
580 if ((job_memory_limit != (size_t)MAX_PTR) ||
581 (job_process_memory_limit != (size_t)MAX_PTR) ||
582 (job_workingset_limit != (size_t)MAX_PTR))
584 job_physical_memory_limit = min (job_memory_limit, job_process_memory_limit);
585 job_physical_memory_limit = min (job_physical_memory_limit, job_workingset_limit);
588 ::GetProcessMemoryLoad(&ms);
589 total_virtual = ms.ullTotalVirtual;
590 total_physical = ms.ullAvailPhys;
592 // A sanity check in case someone set a larger limit than there is actual physical memory.
593 job_physical_memory_limit = (size_t) min (job_physical_memory_limit, ms.ullTotalPhys);
599 if (job_physical_memory_limit == (size_t)MAX_PTR)
601 job_physical_memory_limit = 0;
603 if (hinstKernel32 != 0)
605 FreeLibrary(hinstKernel32);
607 GCGetProcessMemoryInfo = 0;
611 // Check to see if we are limited by VM.
612 if (total_virtual == 0)
615 ::GetProcessMemoryLoad(&ms);
617 total_virtual = ms.ullTotalVirtual;
618 total_physical = ms.ullTotalPhys;
621 if (job_physical_memory_limit != 0)
623 total_physical = job_physical_memory_limit;
626 if (total_virtual < total_physical)
628 if (hinstKernel32 != 0)
630 // We can also free the lib here - if we are limited by VM we will not be calling
631 // GetProcessMemoryInfo.
632 FreeLibrary(hinstKernel32);
633 GCGetProcessMemoryInfo = 0;
635 g_UseRestrictedVirtualMemory = true;
636 job_physical_memory_limit = (size_t)total_virtual;
639 VolatileStore(&g_RestrictedPhysicalMemoryLimit, job_physical_memory_limit);
640 return g_RestrictedPhysicalMemoryLimit;
645 static size_t GetRestrictedPhysicalMemoryLimit()
647 LIMITED_METHOD_CONTRACT;
649 // The limit was cached already
650 if (g_RestrictedPhysicalMemoryLimit != (size_t)MAX_PTR)
651 return g_RestrictedPhysicalMemoryLimit;
653 size_t memory_limit = PAL_GetRestrictedPhysicalMemoryLimit();
655 VolatileStore(&g_RestrictedPhysicalMemoryLimit, memory_limit);
656 return g_RestrictedPhysicalMemoryLimit;
658 #endif // FEATURE_PAL
661 // Get the physical memory that this process can use.
663 // non zero if it has succeeded, 0 if it has failed
665 // PERF TODO: Requires more work to not treat the restricted case to be special.
666 // To be removed before 3.0 ships.
667 uint64_t GCToOSInterface::GetPhysicalMemoryLimit(bool* is_restricted)
669 LIMITED_METHOD_CONTRACT;
672 *is_restricted = false;
674 size_t restricted_limit = GetRestrictedPhysicalMemoryLimit();
675 if (restricted_limit != 0)
679 && !g_UseRestrictedVirtualMemory
682 *is_restricted = true;
684 return restricted_limit;
687 MEMORYSTATUSEX memStatus;
688 ::GetProcessMemoryLoad(&memStatus);
690 return memStatus.ullTotalPhys;
695 // memory_load - A number between 0 and 100 that specifies the approximate percentage of physical memory
696 // that is in use (0 indicates no memory use and 100 indicates full memory use).
697 // available_physical - The amount of physical memory currently available, in bytes.
698 // available_page_file - The maximum amount of memory the current process can commit, in bytes.
700 // Any parameter can be null.
701 void GCToOSInterface::GetMemoryStatus(uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file)
703 LIMITED_METHOD_CONTRACT;
705 uint64_t restricted_limit = GetRestrictedPhysicalMemoryLimit();
706 if (restricted_limit != 0)
708 size_t workingSetSize;
711 if (!g_UseRestrictedVirtualMemory)
713 PROCESS_MEMORY_COUNTERS pmc;
714 status = GCGetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc));
715 workingSetSize = pmc.WorkingSetSize;
718 status = PAL_GetPhysicalMemoryUsed(&workingSetSize);
723 *memory_load = (uint32_t)((float)workingSetSize * 100.0 / (float)restricted_limit);
724 if (available_physical)
726 if(workingSetSize > restricted_limit)
727 *available_physical = 0;
729 *available_physical = restricted_limit - workingSetSize;
731 // Available page file doesn't mean much when physical memory is restricted since
732 // we don't know how much of it is available to this process so we are not going to
733 // bother to make another OS call for it.
734 if (available_page_file)
735 *available_page_file = 0;
742 ::GetProcessMemoryLoad(&ms);
745 if (g_UseRestrictedVirtualMemory)
747 _ASSERTE (ms.ullTotalVirtual == restricted_limit);
748 if (memory_load != NULL)
749 *memory_load = (uint32_t)((float)(ms.ullTotalVirtual - ms.ullAvailVirtual) * 100.0 / (float)ms.ullTotalVirtual);
750 if (available_physical != NULL)
751 *available_physical = ms.ullTotalVirtual;
753 // Available page file isn't helpful when we are restricted by virtual memory
754 // since the amount of memory we can reserve is less than the amount of
755 // memory we can commit.
756 if (available_page_file != NULL)
757 *available_page_file = 0;
760 #endif //!FEATURE_PAL
762 if (memory_load != NULL)
763 *memory_load = ms.dwMemoryLoad;
764 if (available_physical != NULL)
765 *available_physical = ms.ullAvailPhys;
766 if (available_page_file != NULL)
767 *available_page_file = ms.ullAvailPageFile;
771 // Get a high precision performance counter
774 int64_t GCToOSInterface::QueryPerformanceCounter()
776 LIMITED_METHOD_CONTRACT;
779 if (!::QueryPerformanceCounter(&ts))
782 _ASSERTE(!"Fatal Error - cannot query performance counter.");
783 EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); // TODO: fatal error
789 // Get a frequency of the high precision performance counter
791 // The counter frequency
792 int64_t GCToOSInterface::QueryPerformanceFrequency()
794 LIMITED_METHOD_CONTRACT;
796 LARGE_INTEGER frequency;
797 if (!::QueryPerformanceFrequency(&frequency))
800 _ASSERTE(!"Fatal Error - cannot query performance counter.");
801 EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); // TODO: fatal error
804 return frequency.QuadPart;
807 // Get a time stamp with a low precision
809 // Time stamp in milliseconds
810 uint32_t GCToOSInterface::GetLowPrecisionTimeStamp()
812 LIMITED_METHOD_CONTRACT;
814 return ::GetTickCount();
817 uint32_t GCToOSInterface::GetTotalProcessorCount()
819 LIMITED_METHOD_CONTRACT;
821 if (CPUGroupInfo::CanEnableGCCPUGroups())
823 return CPUGroupInfo::GetNumActiveProcessors();
827 return g_SystemInfo.dwNumberOfProcessors;
831 bool GCToOSInterface::CanEnableGCNumaAware()
833 LIMITED_METHOD_CONTRACT;
835 return NumaNodeInfo::CanEnableGCNumaAware() != FALSE;
838 bool GCToOSInterface::GetNumaProcessorNode(uint16_t proc_no, uint16_t *node_no)
840 LIMITED_METHOD_CONTRACT;
842 GroupProcNo groupProcNo(proc_no);
844 PROCESSOR_NUMBER procNumber;
845 procNumber.Group = groupProcNo.GetGroup();
846 procNumber.Number = (BYTE)groupProcNo.GetProcIndex();
847 procNumber.Reserved = 0;
849 return NumaNodeInfo::GetNumaProcessorNodeEx(&procNumber, node_no) != FALSE;
852 // Get processor number and optionally its NUMA node number for the specified heap number
854 // heap_number - heap number to get the result for
855 // proc_no - set to the selected processor number
856 // node_no - set to the NUMA node of the selected processor or to NUMA_NODE_UNDEFINED
858 // true if it succeeded
859 bool GCToOSInterface::GetProcessorForHeap(uint16_t heap_number, uint16_t* proc_no, uint16_t* node_no)
861 bool success = false;
863 if (CPUGroupInfo::CanEnableGCCPUGroups())
866 CPUGroupInfo::GetGroupForProcessor((uint16_t)heap_number, &gn, &gpn);
868 *proc_no = GroupProcNo(gn, gpn).GetCombinedValue();
869 if (GCToOSInterface::CanEnableGCNumaAware())
871 if (!GCToOSInterface::GetNumaProcessorNode(*proc_no, node_no))
873 *node_no = NUMA_NODE_UNDEFINED;
877 { // no numa setting, each cpu group is treated as a node
886 uint8_t proc_number = 0;
887 for (uintptr_t mask = 1; mask != 0; mask <<= 1)
889 if (g_processAffinitySet.Contains(proc_number))
891 if (bit_number == heap_number)
893 *proc_no = GroupProcNo(GroupProcNo::NoGroup, proc_number).GetCombinedValue();
895 if (GCToOSInterface::CanEnableGCNumaAware())
897 if (!GCToOSInterface::GetNumaProcessorNode(proc_number, node_no))
899 *node_no = NUMA_NODE_UNDEFINED;
915 // Initialize the critical section
916 void CLRCriticalSection::Initialize()
919 UnsafeInitializeCriticalSection(&m_cs);
922 // Destroy the critical section
923 void CLRCriticalSection::Destroy()
926 UnsafeDeleteCriticalSection(&m_cs);
929 // Enter the critical section. Blocks until the section can be entered.
930 void CLRCriticalSection::Enter()
933 UnsafeEnterCriticalSection(&m_cs);
936 // Leave the critical section
937 void CLRCriticalSection::Leave()
940 UnsafeLeaveCriticalSection(&m_cs);
943 // An implementatino of GCEvent that delegates to
944 // a CLREvent, which in turn delegates to the PAL. This event
945 // is also host-aware.
958 return !!m_event.IsValid();
965 assert(m_event.IsValid());
966 m_event.CloseEvent();
973 assert(m_event.IsValid());
981 assert(m_event.IsValid());
985 uint32_t Wait(uint32_t timeout, bool alertable)
989 assert(m_event.IsValid());
990 return m_event.Wait(timeout, alertable);
993 bool CreateAutoEvent(bool initialState)
1000 return !!m_event.CreateAutoEventNoThrow(initialState);
1003 bool CreateManualEvent(bool initialState)
1010 return !!m_event.CreateManualEventNoThrow(initialState);
1013 bool CreateOSAutoEvent(bool initialState)
1020 return !!m_event.CreateOSAutoEventNoThrow(initialState);
1023 bool CreateOSManualEvent(bool initialState)
1030 return !!m_event.CreateOSManualEventNoThrow(initialState);
1039 void GCEvent::CloseEvent()
1041 WRAPPER_NO_CONTRACT;
1043 assert(m_impl != nullptr);
1044 m_impl->CloseEvent();
1049 WRAPPER_NO_CONTRACT;
1051 assert(m_impl != nullptr);
1055 void GCEvent::Reset()
1057 WRAPPER_NO_CONTRACT;
1059 assert(m_impl != nullptr);
1063 uint32_t GCEvent::Wait(uint32_t timeout, bool alertable)
1065 WRAPPER_NO_CONTRACT;
1067 assert(m_impl != nullptr);
1068 return m_impl->Wait(timeout, alertable);
1071 bool GCEvent::CreateManualEventNoThrow(bool initialState)
1078 assert(m_impl == nullptr);
1079 NewHolder<GCEvent::Impl> event = new (nothrow) GCEvent::Impl();
1085 event->CreateManualEvent(initialState);
1086 m_impl = event.Extract();
1090 bool GCEvent::CreateAutoEventNoThrow(bool initialState)
1097 assert(m_impl == nullptr);
1098 NewHolder<GCEvent::Impl> event = new (nothrow) GCEvent::Impl();
1104 event->CreateAutoEvent(initialState);
1105 m_impl = event.Extract();
1109 bool GCEvent::CreateOSAutoEventNoThrow(bool initialState)
1116 assert(m_impl == nullptr);
1117 NewHolder<GCEvent::Impl> event = new (nothrow) GCEvent::Impl();
1123 event->CreateOSAutoEvent(initialState);
1124 m_impl = event.Extract();
1128 bool GCEvent::CreateOSManualEventNoThrow(bool initialState)
1135 assert(m_impl == nullptr);
1136 NewHolder<GCEvent::Impl> event = new (nothrow) GCEvent::Impl();
1142 event->CreateOSManualEvent(initialState);
1143 m_impl = event.Extract();