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 "env/gcenv.os.h"
27 #define MAX_PTR ((uint8_t*)(~(ptrdiff_t)0))
29 // Initialize the interface implementation
31 // true if it has succeeded, false if it has failed
32 bool GCToOSInterface::Initialize()
34 LIMITED_METHOD_CONTRACT;
38 // Shutdown the interface implementation
39 void GCToOSInterface::Shutdown()
41 LIMITED_METHOD_CONTRACT;
44 // Get numeric id of the current thread if possible on the
45 // current platform. It is indended for logging purposes only.
47 // Numeric id of the current thread or 0 if the
48 uint64_t GCToOSInterface::GetCurrentThreadIdForLogging()
50 LIMITED_METHOD_CONTRACT;
51 return ::GetCurrentThreadId();
54 // Get id of the process
56 // Id of the current process
57 uint32_t GCToOSInterface::GetCurrentProcessId()
59 LIMITED_METHOD_CONTRACT;
60 return ::GetCurrentProcessId();
63 // Set ideal affinity for the current thread
65 // affinity - ideal processor affinity for the thread
67 // true if it has succeeded, false if it has failed
68 bool GCToOSInterface::SetCurrentThreadIdealAffinity(GCThreadAffinity* affinity)
70 LIMITED_METHOD_CONTRACT;
74 #if !defined(FEATURE_PAL)
75 PROCESSOR_NUMBER proc;
77 if (affinity->Group != -1)
79 proc.Group = (WORD)affinity->Group;
80 proc.Number = (BYTE)affinity->Processor;
83 success = !!SetThreadIdealProcessorEx(GetCurrentThread(), &proc, NULL);
87 if (GetThreadIdealProcessorEx(GetCurrentThread(), &proc))
89 proc.Number = (BYTE)affinity->Processor;
90 success = !!SetThreadIdealProcessorEx(GetCurrentThread(), &proc, &proc);
98 // Get the number of the current processor
99 uint32_t GCToOSInterface::GetCurrentProcessorNumber()
101 LIMITED_METHOD_CONTRACT;
103 _ASSERTE(CanGetCurrentProcessorNumber());
104 return ::GetCurrentProcessorNumber();
107 // Check if the OS supports getting current processor number
108 bool GCToOSInterface::CanGetCurrentProcessorNumber()
110 LIMITED_METHOD_CONTRACT;
113 return PAL_HasGetCurrentProcessorNumber();
115 // on all Windows platforms we support this API exists
120 // Flush write buffers of processors that are executing threads of the current process
121 void GCToOSInterface::FlushProcessWriteBuffers()
123 LIMITED_METHOD_CONTRACT;
124 ::FlushProcessWriteBuffers();
127 // Break into a debugger
128 void GCToOSInterface::DebugBreak()
130 LIMITED_METHOD_CONTRACT;
134 // Get number of logical processors
135 uint32_t GCToOSInterface::GetLogicalCpuCount()
137 LIMITED_METHOD_CONTRACT;
138 return ::GetLogicalCpuCount();
141 // Causes the calling thread to sleep for the specified number of milliseconds
143 // sleepMSec - time to sleep before switching to another thread
144 void GCToOSInterface::Sleep(uint32_t sleepMSec)
146 LIMITED_METHOD_CONTRACT;
147 __SwitchToThread(sleepMSec, 0);
150 // Causes the calling thread to yield execution to another thread that is ready to run on the current processor.
152 // switchCount - number of times the YieldThread was called in a loop
153 void GCToOSInterface::YieldThread(uint32_t switchCount)
155 LIMITED_METHOD_CONTRACT;
156 __SwitchToThread(0, switchCount);
159 // Reserve virtual memory range.
161 // address - starting virtual address, it can be NULL to let the function choose the starting address
162 // size - size of the virtual memory range
163 // alignment - requested memory alignment
164 // flags - flags to control special settings like write watching
166 // Starting virtual address of the reserved range
167 void* GCToOSInterface::VirtualReserve(size_t size, size_t alignment, uint32_t flags)
169 LIMITED_METHOD_CONTRACT;
171 DWORD memFlags = (flags & VirtualReserveFlags::WriteWatch) ? (MEM_RESERVE | MEM_WRITE_WATCH) : MEM_RESERVE;
174 return ::ClrVirtualAlloc(0, size, memFlags, PAGE_READWRITE);
178 return ::ClrVirtualAllocAligned(0, size, memFlags, PAGE_READWRITE, alignment);
182 // Release virtual memory range previously reserved using VirtualReserve
184 // address - starting virtual address
185 // size - size of the virtual memory range
187 // true if it has succeeded, false if it has failed
188 bool GCToOSInterface::VirtualRelease(void* address, size_t size)
190 LIMITED_METHOD_CONTRACT;
192 UNREFERENCED_PARAMETER(size);
193 return !!::ClrVirtualFree(address, 0, MEM_RELEASE);
196 // Commit virtual memory range. It must be part of a range reserved using VirtualReserve.
198 // address - starting virtual address
199 // size - size of the virtual memory range
201 // true if it has succeeded, false if it has failed
202 bool GCToOSInterface::VirtualCommit(void* address, size_t size)
204 LIMITED_METHOD_CONTRACT;
206 return ::ClrVirtualAlloc(address, size, MEM_COMMIT, PAGE_READWRITE) != NULL;
209 // Decomit virtual memory range.
211 // address - starting virtual address
212 // size - size of the virtual memory range
214 // true if it has succeeded, false if it has failed
215 bool GCToOSInterface::VirtualDecommit(void* address, size_t size)
217 LIMITED_METHOD_CONTRACT;
219 return !!::ClrVirtualFree(address, size, MEM_DECOMMIT);
222 // Reset virtual memory range. Indicates that data in the memory range specified by address and size is no
223 // longer of interest, but it should not be decommitted.
225 // address - starting virtual address
226 // size - size of the virtual memory range
227 // unlock - true if the memory range should also be unlocked
229 // true if it has succeeded, false if it has failed
230 bool GCToOSInterface::VirtualReset(void * address, size_t size, bool unlock)
232 LIMITED_METHOD_CONTRACT;
234 bool success = ::ClrVirtualAlloc(address, size, MEM_RESET, PAGE_READWRITE) != NULL;
236 if (success && unlock)
238 // Remove the page range from the working set
239 ::VirtualUnlock(address, size);
241 #endif // FEATURE_PAL
246 // Check if the OS supports write watching
247 bool GCToOSInterface::SupportsWriteWatch()
249 LIMITED_METHOD_CONTRACT;
251 bool writeWatchSupported = false;
253 // check if the OS supports write-watch.
254 // Drawbridge does not support write-watch so we still need to do the runtime detection for them.
255 // Otherwise, all currently supported OSes do support write-watch.
256 void* mem = VirtualReserve (g_SystemInfo.dwAllocationGranularity, 0, VirtualReserveFlags::WriteWatch);
259 VirtualRelease (mem, g_SystemInfo.dwAllocationGranularity);
260 writeWatchSupported = true;
263 return writeWatchSupported;
266 // Reset the write tracking state for the specified virtual memory range.
268 // address - starting virtual address
269 // size - size of the virtual memory range
270 void GCToOSInterface::ResetWriteWatch(void* address, size_t size)
272 LIMITED_METHOD_CONTRACT;
274 ::ResetWriteWatch(address, size);
277 // Retrieve addresses of the pages that are written to in a region of virtual memory
279 // resetState - true indicates to reset the write tracking state
280 // address - starting virtual address
281 // size - size of the virtual memory range
282 // pageAddresses - buffer that receives an array of page addresses in the memory region
283 // pageAddressesCount - on input, size of the lpAddresses array, in array elements
284 // on output, the number of page addresses that are returned in the array.
286 // true if it has succeeded, false if it has failed
287 bool GCToOSInterface::GetWriteWatch(bool resetState, void* address, size_t size, void** pageAddresses, uintptr_t* pageAddressesCount)
289 LIMITED_METHOD_CONTRACT;
291 uint32_t flags = resetState ? 1 : 0;
294 bool success = ::GetWriteWatch(flags, address, size, pageAddresses, (ULONG_PTR*)pageAddressesCount, &granularity) == 0;
295 _ASSERTE (granularity == OS_PAGE_SIZE);
300 // Get size of the largest cache on the processor die
302 // trueSize - true to return true cache size, false to return scaled up size based on
303 // the processor architecture
306 size_t GCToOSInterface::GetLargestOnDieCacheSize(bool trueSize)
308 LIMITED_METHOD_CONTRACT;
310 return ::GetLargestOnDieCacheSize(trueSize);
313 // Get affinity mask of the current process
315 // processMask - affinity mask for the specified process
316 // systemMask - affinity mask for the system
318 // true if it has succeeded, false if it has failed
320 // A process affinity mask is a bit vector in which each bit represents the processors that
321 // a process is allowed to run on. A system affinity mask is a bit vector in which each bit
322 // represents the processors that are configured into a system.
323 // A process affinity mask is a subset of the system affinity mask. A process is only allowed
324 // to run on the processors configured into a system. Therefore, the process affinity mask cannot
325 // specify a 1 bit for a processor when the system affinity mask specifies a 0 bit for that processor.
326 bool GCToOSInterface::GetCurrentProcessAffinityMask(uintptr_t* processMask, uintptr_t* systemMask)
328 LIMITED_METHOD_CONTRACT;
331 return !!::GetProcessAffinityMask(GetCurrentProcess(), (PDWORD_PTR)processMask, (PDWORD_PTR)systemMask);
337 // Get number of processors assigned to the current process
339 // The number of processors
340 uint32_t GCToOSInterface::GetCurrentProcessCpuCount()
342 LIMITED_METHOD_CONTRACT;
344 return ::GetCurrentProcessCpuCount();
347 // Return the size of the user-mode portion of the virtual address space of this process.
349 // non zero if it has succeeded, 0 if it has failed
350 size_t GCToOSInterface::GetVirtualMemoryLimit()
352 LIMITED_METHOD_CONTRACT;
354 MEMORYSTATUSEX memStatus;
355 ::GetProcessMemoryLoad(&memStatus);
357 return (size_t)memStatus.ullTotalVirtual;
363 typedef BOOL (WINAPI *PGET_PROCESS_MEMORY_INFO)(HANDLE handle, PROCESS_MEMORY_COUNTERS* memCounters, uint32_t cb);
364 static PGET_PROCESS_MEMORY_INFO GCGetProcessMemoryInfo = 0;
366 static size_t g_RestrictedPhysicalMemoryLimit = (size_t)MAX_PTR;
368 typedef BOOL (WINAPI *PIS_PROCESS_IN_JOB)(HANDLE processHandle, HANDLE jobHandle, BOOL* result);
369 typedef BOOL (WINAPI *PQUERY_INFORMATION_JOB_OBJECT)(HANDLE jobHandle, JOBOBJECTINFOCLASS jobObjectInfoClass, void* lpJobObjectInfo, DWORD cbJobObjectInfoLength, LPDWORD lpReturnLength);
371 static size_t GetRestrictedPhysicalMemoryLimit()
373 LIMITED_METHOD_CONTRACT;
375 // The limit was cached already
376 if (g_RestrictedPhysicalMemoryLimit != (size_t)MAX_PTR)
377 return g_RestrictedPhysicalMemoryLimit;
379 size_t job_physical_memory_limit = (size_t)MAX_PTR;
380 BOOL in_job_p = FALSE;
381 HINSTANCE hinstKernel32 = 0;
383 PIS_PROCESS_IN_JOB GCIsProcessInJob = 0;
384 PQUERY_INFORMATION_JOB_OBJECT GCQueryInformationJobObject = 0;
386 GCIsProcessInJob = &(::IsProcessInJob);
388 if (!GCIsProcessInJob(GetCurrentProcess(), NULL, &in_job_p))
393 hinstKernel32 = WszLoadLibrary(L"kernel32.dll");
397 GCGetProcessMemoryInfo = (PGET_PROCESS_MEMORY_INFO)GetProcAddress(hinstKernel32, "K32GetProcessMemoryInfo");
399 if (!GCGetProcessMemoryInfo)
402 GCQueryInformationJobObject = &(::QueryInformationJobObject);
404 if (!GCQueryInformationJobObject)
407 JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info;
408 if (GCQueryInformationJobObject (NULL, JobObjectExtendedLimitInformation, &limit_info,
409 sizeof(limit_info), NULL))
411 size_t job_memory_limit = (size_t)MAX_PTR;
412 size_t job_process_memory_limit = (size_t)MAX_PTR;
413 size_t job_workingset_limit = (size_t)MAX_PTR;
415 // Notes on the NT job object:
417 // You can specific a bigger process commit or working set limit than
418 // job limit which is pointless so we use the smallest of all 3 as
419 // to calculate our "physical memory load" or "available physical memory"
420 // when running inside a job object, ie, we treat this as the amount of physical memory
421 // our process is allowed to use.
423 // The commit limit is already reflected by default when you run in a
424 // job but the physical memory load is not.
426 if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_JOB_MEMORY) != 0)
427 job_memory_limit = limit_info.JobMemoryLimit;
428 if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_PROCESS_MEMORY) != 0)
429 job_process_memory_limit = limit_info.ProcessMemoryLimit;
430 if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_WORKINGSET) != 0)
431 job_workingset_limit = limit_info.BasicLimitInformation.MaximumWorkingSetSize;
433 job_physical_memory_limit = min (job_memory_limit, job_process_memory_limit);
434 job_physical_memory_limit = min (job_physical_memory_limit, job_workingset_limit);
437 ::GetProcessMemoryLoad(&ms);
439 // A sanity check in case someone set a larger limit than there is actual physical memory.
440 job_physical_memory_limit = (size_t) min (job_physical_memory_limit, ms.ullTotalPhys);
445 if (job_physical_memory_limit == (size_t)MAX_PTR)
447 job_physical_memory_limit = 0;
449 FreeLibrary(hinstKernel32);
452 VolatileStore(&g_RestrictedPhysicalMemoryLimit, job_physical_memory_limit);
453 return g_RestrictedPhysicalMemoryLimit;
456 #endif // FEATURE_PAL
459 // Get the physical memory that this process can use.
461 // non zero if it has succeeded, 0 if it has failed
462 uint64_t GCToOSInterface::GetPhysicalMemoryLimit()
464 LIMITED_METHOD_CONTRACT;
467 size_t restricted_limit = GetRestrictedPhysicalMemoryLimit();
468 if (restricted_limit != 0)
469 return restricted_limit;
472 MEMORYSTATUSEX memStatus;
473 ::GetProcessMemoryLoad(&memStatus);
475 return memStatus.ullTotalPhys;
480 // memory_load - A number between 0 and 100 that specifies the approximate percentage of physical memory
481 // that is in use (0 indicates no memory use and 100 indicates full memory use).
482 // available_physical - The amount of physical memory currently available, in bytes.
483 // available_page_file - The maximum amount of memory the current process can commit, in bytes.
485 // Any parameter can be null.
486 void GCToOSInterface::GetMemoryStatus(uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file)
488 LIMITED_METHOD_CONTRACT;
491 uint64_t restricted_limit = GetRestrictedPhysicalMemoryLimit();
492 if (restricted_limit != 0)
494 PROCESS_MEMORY_COUNTERS pmc;
495 if (GCGetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)))
498 *memory_load = (uint32_t)((float)pmc.WorkingSetSize * 100.0 / (float)restricted_limit);
499 if (available_physical)
500 *available_physical = restricted_limit - pmc.WorkingSetSize;
501 // Available page file doesn't mean much when physical memory is restricted since
502 // we don't know how much of it is available to this process so we are not going to
503 // bother to make another OS call for it.
504 if (available_page_file)
505 *available_page_file = 0;
513 ::GetProcessMemoryLoad(&ms);
515 if (memory_load != NULL)
516 *memory_load = ms.dwMemoryLoad;
517 if (available_physical != NULL)
518 *available_physical = ms.ullAvailPhys;
519 if (available_page_file != NULL)
520 *available_page_file = ms.ullAvailPageFile;
523 // Get a high precision performance counter
526 int64_t GCToOSInterface::QueryPerformanceCounter()
528 LIMITED_METHOD_CONTRACT;
531 if (!::QueryPerformanceCounter(&ts))
534 _ASSERTE(!"Fatal Error - cannot query performance counter.");
535 EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); // TODO: fatal error
541 // Get a frequency of the high precision performance counter
543 // The counter frequency
544 int64_t GCToOSInterface::QueryPerformanceFrequency()
546 LIMITED_METHOD_CONTRACT;
548 LARGE_INTEGER frequency;
549 if (!::QueryPerformanceFrequency(&frequency))
552 _ASSERTE(!"Fatal Error - cannot query performance counter.");
553 EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); // TODO: fatal error
556 return frequency.QuadPart;
559 // Get a time stamp with a low precision
561 // Time stamp in milliseconds
562 uint32_t GCToOSInterface::GetLowPrecisionTimeStamp()
564 LIMITED_METHOD_CONTRACT;
566 return ::GetTickCount();
569 // Parameters of the GC thread stub
570 struct GCThreadStubParam
572 GCThreadFunction GCThreadFunction;
576 // GC thread stub to convert GC thread function to an OS specific thread function
577 static DWORD __stdcall GCThreadStub(void* param)
581 GCThreadStubParam *stubParam = (GCThreadStubParam*)param;
582 GCThreadFunction function = stubParam->GCThreadFunction;
583 void* threadParam = stubParam->GCThreadParam;
587 function(threadParam);
592 // Create a new thread
594 // function - the function to be executed by the thread
595 // param - parameters of the thread
596 // affinity - processor affinity of the thread
598 // true if it has succeeded, false if it has failed
599 bool GCToOSInterface::CreateThread(GCThreadFunction function, void* param, GCThreadAffinity* affinity)
601 LIMITED_METHOD_CONTRACT;
605 NewHolder<GCThreadStubParam> stubParam = new (nothrow) GCThreadStubParam();
606 if (stubParam == NULL)
611 stubParam->GCThreadFunction = function;
612 stubParam->GCThreadParam = param;
614 HANDLE gc_thread = Thread::CreateUtilityThread(Thread::StackSize_Medium, GCThreadStub, stubParam, CREATE_SUSPENDED, (DWORD*)&thread_id);
621 stubParam.SuppressRelease();
623 SetThreadPriority(gc_thread, /* THREAD_PRIORITY_ABOVE_NORMAL );*/ THREAD_PRIORITY_HIGHEST );
626 if (affinity->Group != GCThreadAffinity::None)
628 _ASSERTE(affinity->Processor != GCThreadAffinity::None);
630 ga.Group = (WORD)affinity->Group;
631 ga.Reserved[0] = 0; // reserve must be filled with zero
632 ga.Reserved[1] = 0; // otherwise call may fail
634 ga.Mask = (size_t)1 << affinity->Processor;
636 CPUGroupInfo::SetThreadGroupAffinity(gc_thread, &ga, NULL);
638 else if (affinity->Processor != GCThreadAffinity::None)
640 SetThreadAffinityMask(gc_thread, (DWORD_PTR)1 << affinity->Processor);
642 #endif // !FEATURE_PAL
644 ResumeThread(gc_thread);
645 CloseHandle(gc_thread);
650 // Initialize the critical section
651 void CLRCriticalSection::Initialize()
654 UnsafeInitializeCriticalSection(&m_cs);
657 // Destroy the critical section
658 void CLRCriticalSection::Destroy()
661 UnsafeDeleteCriticalSection(&m_cs);
664 // Enter the critical section. Blocks until the section can be entered.
665 void CLRCriticalSection::Enter()
668 UnsafeEnterCriticalSection(&m_cs);
671 // Leave the critical section
672 void CLRCriticalSection::Leave()
675 UnsafeLeaveCriticalSection(&m_cs);