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.
4
5/*
6 * gcenv.os.cpp
7 *
8 * GCToOSInterface implementation
9 *
10
11 *
12 */
13
14#include "common.h"
15#include "gcenv.h"
16
17#ifndef FEATURE_PAL
18#include <Psapi.h>
19#endif
20
21#ifdef Sleep
22#undef Sleep
23#endif // Sleep
24
25#include "../gc/env/gcenv.os.h"
26
27#define MAX_PTR ((uint8_t*)(~(ptrdiff_t)0))
28
29#ifdef FEATURE_PAL
30uint32_t g_pageSizeUnixInl = 0;
31#endif
32
33
34// Initialize the interface implementation
35// Return:
36// true if it has succeeded, false if it has failed
37bool GCToOSInterface::Initialize()
38{
39 LIMITED_METHOD_CONTRACT;
40
41#ifdef FEATURE_PAL
42 g_pageSizeUnixInl = GetOsPageSize();
43#endif
44
45 return true;
46}
47
48// Shutdown the interface implementation
49void GCToOSInterface::Shutdown()
50{
51 LIMITED_METHOD_CONTRACT;
52}
53
54// Get numeric id of the current thread if possible on the
55// current platform. It is indended for logging purposes only.
56// Return:
57// Numeric id of the current thread or 0 if the
58uint64_t GCToOSInterface::GetCurrentThreadIdForLogging()
59{
60 LIMITED_METHOD_CONTRACT;
61 return ::GetCurrentThreadId();
62}
63
64// Get id of the process
65// Return:
66// Id of the current process
67uint32_t GCToOSInterface::GetCurrentProcessId()
68{
69 LIMITED_METHOD_CONTRACT;
70 return ::GetCurrentProcessId();
71}
72
73// Set ideal affinity for the current thread
74// Parameters:
75// affinity - ideal processor affinity for the thread
76// Return:
77// true if it has succeeded, false if it has failed
78bool GCToOSInterface::SetCurrentThreadIdealAffinity(GCThreadAffinity* affinity)
79{
80 LIMITED_METHOD_CONTRACT;
81
82 bool success = true;
83
84#if !defined(FEATURE_CORESYSTEM)
85 SetThreadIdealProcessor(GetCurrentThread(), (DWORD)affinity->Processor);
86#else
87 PROCESSOR_NUMBER proc;
88
89 if (affinity->Group != -1)
90 {
91 proc.Group = (WORD)affinity->Group;
92 proc.Number = (BYTE)affinity->Processor;
93 proc.Reserved = 0;
94
95 success = !!SetThreadIdealProcessorEx(GetCurrentThread(), &proc, NULL);
96 }
97#if !defined(FEATURE_PAL)
98 else
99 {
100 if (GetThreadIdealProcessorEx(GetCurrentThread(), &proc))
101 {
102 proc.Number = (BYTE)affinity->Processor;
103 success = !!SetThreadIdealProcessorEx(GetCurrentThread(), &proc, &proc);
104 }
105 }
106#endif // !defined(FEATURE_PAL)
107#endif
108
109 return success;
110}
111
112// Get the number of the current processor
113uint32_t GCToOSInterface::GetCurrentProcessorNumber()
114{
115 LIMITED_METHOD_CONTRACT;
116
117 _ASSERTE(CanGetCurrentProcessorNumber());
118 return ::GetCurrentProcessorNumber();
119}
120
121// Check if the OS supports getting current processor number
122bool GCToOSInterface::CanGetCurrentProcessorNumber()
123{
124 LIMITED_METHOD_CONTRACT;
125
126#ifdef FEATURE_PAL
127 return PAL_HasGetCurrentProcessorNumber();
128#else
129 // on all Windows platforms we support this API exists
130 return true;
131#endif
132}
133
134// Flush write buffers of processors that are executing threads of the current process
135void GCToOSInterface::FlushProcessWriteBuffers()
136{
137 LIMITED_METHOD_CONTRACT;
138 ::FlushProcessWriteBuffers();
139}
140
141// Break into a debugger
142void GCToOSInterface::DebugBreak()
143{
144 LIMITED_METHOD_CONTRACT;
145 ::DebugBreak();
146}
147
148// Causes the calling thread to sleep for the specified number of milliseconds
149// Parameters:
150// sleepMSec - time to sleep before switching to another thread
151void GCToOSInterface::Sleep(uint32_t sleepMSec)
152{
153 LIMITED_METHOD_CONTRACT;
154 __SwitchToThread(sleepMSec, 0);
155}
156
157// Causes the calling thread to yield execution to another thread that is ready to run on the current processor.
158// Parameters:
159// switchCount - number of times the YieldThread was called in a loop
160void GCToOSInterface::YieldThread(uint32_t switchCount)
161{
162 LIMITED_METHOD_CONTRACT;
163 __SwitchToThread(0, switchCount);
164}
165
166// Reserve virtual memory range.
167// Parameters:
168// address - starting virtual address, it can be NULL to let the function choose the starting address
169// size - size of the virtual memory range
170// alignment - requested memory alignment
171// flags - flags to control special settings like write watching
172// Return:
173// Starting virtual address of the reserved range
174void* GCToOSInterface::VirtualReserve(size_t size, size_t alignment, uint32_t flags)
175{
176 LIMITED_METHOD_CONTRACT;
177
178 DWORD memFlags = (flags & VirtualReserveFlags::WriteWatch) ? (MEM_RESERVE | MEM_WRITE_WATCH) : MEM_RESERVE;
179
180 // This is not strictly necessary for a correctness standpoint. Windows already guarantees
181 // allocation granularity alignment when using MEM_RESERVE, so aligning the size here has no effect.
182 // However, ClrVirtualAlloc does expect the size to be aligned to the allocation granularity.
183 size_t aligned_size = (size + g_SystemInfo.dwAllocationGranularity - 1) & ~static_cast<size_t>(g_SystemInfo.dwAllocationGranularity - 1);
184 if (alignment == 0)
185 {
186 return ::ClrVirtualAlloc(0, aligned_size, memFlags, PAGE_READWRITE);
187 }
188 else
189 {
190 return ::ClrVirtualAllocAligned(0, aligned_size, memFlags, PAGE_READWRITE, alignment);
191 }
192}
193
194// Release virtual memory range previously reserved using VirtualReserve
195// Parameters:
196// address - starting virtual address
197// size - size of the virtual memory range
198// Return:
199// true if it has succeeded, false if it has failed
200bool GCToOSInterface::VirtualRelease(void* address, size_t size)
201{
202 LIMITED_METHOD_CONTRACT;
203
204 UNREFERENCED_PARAMETER(size);
205 return !!::ClrVirtualFree(address, 0, MEM_RELEASE);
206}
207
208// Commit virtual memory range. It must be part of a range reserved using VirtualReserve.
209// Parameters:
210// address - starting virtual address
211// size - size of the virtual memory range
212// Return:
213// true if it has succeeded, false if it has failed
214bool GCToOSInterface::VirtualCommit(void* address, size_t size, uint32_t node)
215{
216 LIMITED_METHOD_CONTRACT;
217
218 if (node == NUMA_NODE_UNDEFINED)
219 {
220 return ::ClrVirtualAlloc(address, size, MEM_COMMIT, PAGE_READWRITE) != NULL;
221 }
222 else
223 {
224 return NumaNodeInfo::VirtualAllocExNuma(::GetCurrentProcess(), address, size, MEM_COMMIT, PAGE_READWRITE, node) != NULL;
225 }
226}
227
228// Decomit virtual memory range.
229// Parameters:
230// address - starting virtual address
231// size - size of the virtual memory range
232// Return:
233// true if it has succeeded, false if it has failed
234bool GCToOSInterface::VirtualDecommit(void* address, size_t size)
235{
236 LIMITED_METHOD_CONTRACT;
237
238 return !!::ClrVirtualFree(address, size, MEM_DECOMMIT);
239}
240
241// Reset virtual memory range. Indicates that data in the memory range specified by address and size is no
242// longer of interest, but it should not be decommitted.
243// Parameters:
244// address - starting virtual address
245// size - size of the virtual memory range
246// unlock - true if the memory range should also be unlocked
247// Return:
248// true if it has succeeded, false if it has failed
249bool GCToOSInterface::VirtualReset(void * address, size_t size, bool unlock)
250{
251 LIMITED_METHOD_CONTRACT;
252
253 bool success = ::ClrVirtualAlloc(address, size, MEM_RESET, PAGE_READWRITE) != NULL;
254#ifndef FEATURE_PAL
255 if (success && unlock)
256 {
257 // Remove the page range from the working set
258 ::VirtualUnlock(address, size);
259 }
260#endif // FEATURE_PAL
261
262 return success;
263}
264
265// Check if the OS supports write watching
266bool GCToOSInterface::SupportsWriteWatch()
267{
268 LIMITED_METHOD_CONTRACT;
269
270 bool writeWatchSupported = false;
271
272 // check if the OS supports write-watch.
273 // Drawbridge does not support write-watch so we still need to do the runtime detection for them.
274 // Otherwise, all currently supported OSes do support write-watch.
275 void* mem = VirtualReserve (g_SystemInfo.dwAllocationGranularity, 0, VirtualReserveFlags::WriteWatch);
276 if (mem != NULL)
277 {
278 VirtualRelease (mem, g_SystemInfo.dwAllocationGranularity);
279 writeWatchSupported = true;
280 }
281
282 return writeWatchSupported;
283}
284
285// Reset the write tracking state for the specified virtual memory range.
286// Parameters:
287// address - starting virtual address
288// size - size of the virtual memory range
289void GCToOSInterface::ResetWriteWatch(void* address, size_t size)
290{
291 LIMITED_METHOD_CONTRACT;
292
293 ::ResetWriteWatch(address, size);
294}
295
296// Retrieve addresses of the pages that are written to in a region of virtual memory
297// Parameters:
298// resetState - true indicates to reset the write tracking state
299// address - starting virtual address
300// size - size of the virtual memory range
301// pageAddresses - buffer that receives an array of page addresses in the memory region
302// pageAddressesCount - on input, size of the lpAddresses array, in array elements
303// on output, the number of page addresses that are returned in the array.
304// Return:
305// true if it has succeeded, false if it has failed
306bool GCToOSInterface::GetWriteWatch(bool resetState, void* address, size_t size, void** pageAddresses, uintptr_t* pageAddressesCount)
307{
308 LIMITED_METHOD_CONTRACT;
309
310 uint32_t flags = resetState ? 1 : 0;
311 ULONG granularity;
312
313 bool success = ::GetWriteWatch(flags, address, size, pageAddresses, (ULONG_PTR*)pageAddressesCount, &granularity) == 0;
314 _ASSERTE (granularity == GetOsPageSize());
315
316 return success;
317}
318
319// Get size of the largest cache on the processor die
320// Parameters:
321// trueSize - true to return true cache size, false to return scaled up size based on
322// the processor architecture
323// Return:
324// Size of the cache
325size_t GCToOSInterface::GetCacheSizePerLogicalCpu(bool trueSize)
326{
327 LIMITED_METHOD_CONTRACT;
328
329 return ::GetCacheSizePerLogicalCpu(trueSize);
330}
331
332// Sets the calling thread's affinity to only run on the processor specified
333// in the GCThreadAffinity structure.
334// Parameters:
335// affinity - The requested affinity for the calling thread. At most one processor
336// can be provided.
337// Return:
338// true if setting the affinity was successful, false otherwise.
339bool GCToOSInterface::SetThreadAffinity(GCThreadAffinity* affinity)
340{
341 LIMITED_METHOD_CONTRACT;
342
343 assert(affinity != nullptr);
344 if (affinity->Group != GCThreadAffinity::None)
345 {
346 assert(affinity->Processor != GCThreadAffinity::None);
347
348 GROUP_AFFINITY ga;
349 ga.Group = (WORD)affinity->Group;
350 ga.Reserved[0] = 0; // reserve must be filled with zero
351 ga.Reserved[1] = 0; // otherwise call may fail
352 ga.Reserved[2] = 0;
353 ga.Mask = (size_t)1 << affinity->Processor;
354 return !!SetThreadGroupAffinity(GetCurrentThread(), &ga, nullptr);
355 }
356 else if (affinity->Processor != GCThreadAffinity::None)
357 {
358 return !!SetThreadAffinityMask(GetCurrentThread(), (DWORD_PTR)1 << affinity->Processor);
359 }
360
361 // Given affinity must specify at least one processor to use.
362 return false;
363}
364
365// Boosts the calling thread's thread priority to a level higher than the default
366// for new threads.
367// Parameters:
368// None.
369// Return:
370// true if the priority boost was successful, false otherwise.
371bool GCToOSInterface::BoostThreadPriority()
372{
373 return !!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
374}
375
376// Get affinity mask of the current process
377// Parameters:
378// processMask - affinity mask for the specified process
379// systemMask - affinity mask for the system
380// Return:
381// true if it has succeeded, false if it has failed
382// Remarks:
383// A process affinity mask is a bit vector in which each bit represents the processors that
384// a process is allowed to run on. A system affinity mask is a bit vector in which each bit
385// represents the processors that are configured into a system.
386// A process affinity mask is a subset of the system affinity mask. A process is only allowed
387// to run on the processors configured into a system. Therefore, the process affinity mask cannot
388// specify a 1 bit for a processor when the system affinity mask specifies a 0 bit for that processor.
389bool GCToOSInterface::GetCurrentProcessAffinityMask(uintptr_t* processMask, uintptr_t* systemMask)
390{
391 LIMITED_METHOD_CONTRACT;
392
393 return !!::GetProcessAffinityMask(GetCurrentProcess(), (PDWORD_PTR)processMask, (PDWORD_PTR)systemMask);
394}
395
396// Get number of processors assigned to the current process
397// Return:
398// The number of processors
399uint32_t GCToOSInterface::GetCurrentProcessCpuCount()
400{
401 LIMITED_METHOD_CONTRACT;
402
403 return ::GetCurrentProcessCpuCount();
404}
405
406// Return the size of the user-mode portion of the virtual address space of this process.
407// Return:
408// non zero if it has succeeded, 0 if it has failed
409size_t GCToOSInterface::GetVirtualMemoryLimit()
410{
411 LIMITED_METHOD_CONTRACT;
412
413 MEMORYSTATUSEX memStatus;
414 ::GetProcessMemoryLoad(&memStatus);
415
416 return (size_t)memStatus.ullTotalVirtual;
417}
418
419static size_t g_RestrictedPhysicalMemoryLimit = (size_t)MAX_PTR;
420
421#ifndef FEATURE_PAL
422
423// For 32-bit processes the virtual address range could be smaller than the amount of physical
424// memory on the machine/in the container, we need to restrict by the VM.
425static bool g_UseRestrictedVirtualMemory = false;
426
427typedef BOOL (WINAPI *PGET_PROCESS_MEMORY_INFO)(HANDLE handle, PROCESS_MEMORY_COUNTERS* memCounters, uint32_t cb);
428static PGET_PROCESS_MEMORY_INFO GCGetProcessMemoryInfo = 0;
429
430typedef BOOL (WINAPI *PIS_PROCESS_IN_JOB)(HANDLE processHandle, HANDLE jobHandle, BOOL* result);
431typedef BOOL (WINAPI *PQUERY_INFORMATION_JOB_OBJECT)(HANDLE jobHandle, JOBOBJECTINFOCLASS jobObjectInfoClass, void* lpJobObjectInfo, DWORD cbJobObjectInfoLength, LPDWORD lpReturnLength);
432
433static size_t GetRestrictedPhysicalMemoryLimit()
434{
435 LIMITED_METHOD_CONTRACT;
436
437 // The limit was cached already
438 if (g_RestrictedPhysicalMemoryLimit != (size_t)MAX_PTR)
439 return g_RestrictedPhysicalMemoryLimit;
440
441 size_t job_physical_memory_limit = (size_t)MAX_PTR;
442 uint64_t total_virtual = 0;
443 uint64_t total_physical = 0;
444 BOOL in_job_p = FALSE;
445 HINSTANCE hinstKernel32 = 0;
446
447 PIS_PROCESS_IN_JOB GCIsProcessInJob = 0;
448 PQUERY_INFORMATION_JOB_OBJECT GCQueryInformationJobObject = 0;
449
450 GCIsProcessInJob = &(::IsProcessInJob);
451
452 if (!GCIsProcessInJob(GetCurrentProcess(), NULL, &in_job_p))
453 goto exit;
454
455 if (in_job_p)
456 {
457 hinstKernel32 = WszLoadLibrary(L"kernel32.dll");
458 if (!hinstKernel32)
459 goto exit;
460
461 GCGetProcessMemoryInfo = (PGET_PROCESS_MEMORY_INFO)GetProcAddress(hinstKernel32, "K32GetProcessMemoryInfo");
462
463 if (!GCGetProcessMemoryInfo)
464 goto exit;
465
466 GCQueryInformationJobObject = &(::QueryInformationJobObject);
467
468 if (!GCQueryInformationJobObject)
469 goto exit;
470
471 JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info;
472 if (GCQueryInformationJobObject (NULL, JobObjectExtendedLimitInformation, &limit_info,
473 sizeof(limit_info), NULL))
474 {
475 size_t job_memory_limit = (size_t)MAX_PTR;
476 size_t job_process_memory_limit = (size_t)MAX_PTR;
477 size_t job_workingset_limit = (size_t)MAX_PTR;
478
479 // Notes on the NT job object:
480 //
481 // You can specific a bigger process commit or working set limit than
482 // job limit which is pointless so we use the smallest of all 3 as
483 // to calculate our "physical memory load" or "available physical memory"
484 // when running inside a job object, ie, we treat this as the amount of physical memory
485 // our process is allowed to use.
486 //
487 // The commit limit is already reflected by default when you run in a
488 // job but the physical memory load is not.
489 //
490 if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_JOB_MEMORY) != 0)
491 job_memory_limit = limit_info.JobMemoryLimit;
492 if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_PROCESS_MEMORY) != 0)
493 job_process_memory_limit = limit_info.ProcessMemoryLimit;
494 if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_WORKINGSET) != 0)
495 job_workingset_limit = limit_info.BasicLimitInformation.MaximumWorkingSetSize;
496
497 job_physical_memory_limit = min (job_memory_limit, job_process_memory_limit);
498 job_physical_memory_limit = min (job_physical_memory_limit, job_workingset_limit);
499
500 MEMORYSTATUSEX ms;
501 ::GetProcessMemoryLoad(&ms);
502 total_virtual = ms.ullTotalVirtual;
503 total_physical = ms.ullAvailPhys;
504
505 // A sanity check in case someone set a larger limit than there is actual physical memory.
506 job_physical_memory_limit = (size_t) min (job_physical_memory_limit, ms.ullTotalPhys);
507 }
508 }
509
510exit:
511 if (job_physical_memory_limit == (size_t)MAX_PTR)
512 {
513 job_physical_memory_limit = 0;
514
515 if (hinstKernel32 != 0)
516 {
517 FreeLibrary(hinstKernel32);
518 hinstKernel32 = 0;
519 GCGetProcessMemoryInfo = 0;
520 }
521 }
522
523 // Check to see if we are limited by VM.
524 if (total_virtual == 0)
525 {
526 MEMORYSTATUSEX ms;
527 ::GetProcessMemoryLoad(&ms);
528
529 total_virtual = ms.ullTotalVirtual;
530 total_physical = ms.ullTotalPhys;
531 }
532
533 if (job_physical_memory_limit != 0)
534 {
535 total_physical = job_physical_memory_limit;
536 }
537
538 if (total_virtual < total_physical)
539 {
540 if (hinstKernel32 != 0)
541 {
542 // We can also free the lib here - if we are limited by VM we will not be calling
543 // GetProcessMemoryInfo.
544 FreeLibrary(hinstKernel32);
545 GCGetProcessMemoryInfo = 0;
546 }
547 g_UseRestrictedVirtualMemory = true;
548 job_physical_memory_limit = (size_t)total_virtual;
549 }
550
551 VolatileStore(&g_RestrictedPhysicalMemoryLimit, job_physical_memory_limit);
552 return g_RestrictedPhysicalMemoryLimit;
553}
554
555#else
556
557static size_t GetRestrictedPhysicalMemoryLimit()
558{
559 LIMITED_METHOD_CONTRACT;
560
561 // The limit was cached already
562 if (g_RestrictedPhysicalMemoryLimit != (size_t)MAX_PTR)
563 return g_RestrictedPhysicalMemoryLimit;
564
565 size_t memory_limit = PAL_GetRestrictedPhysicalMemoryLimit();
566
567 VolatileStore(&g_RestrictedPhysicalMemoryLimit, memory_limit);
568 return g_RestrictedPhysicalMemoryLimit;
569}
570#endif // FEATURE_PAL
571
572
573// Get the physical memory that this process can use.
574// Return:
575// non zero if it has succeeded, 0 if it has failed
576uint64_t GCToOSInterface::GetPhysicalMemoryLimit()
577{
578 LIMITED_METHOD_CONTRACT;
579
580 size_t restricted_limit = GetRestrictedPhysicalMemoryLimit();
581 if (restricted_limit != 0)
582 return restricted_limit;
583
584 MEMORYSTATUSEX memStatus;
585 ::GetProcessMemoryLoad(&memStatus);
586
587 return memStatus.ullTotalPhys;
588}
589
590// Get memory status
591// Parameters:
592// memory_load - A number between 0 and 100 that specifies the approximate percentage of physical memory
593// that is in use (0 indicates no memory use and 100 indicates full memory use).
594// available_physical - The amount of physical memory currently available, in bytes.
595// available_page_file - The maximum amount of memory the current process can commit, in bytes.
596// Remarks:
597// Any parameter can be null.
598void GCToOSInterface::GetMemoryStatus(uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file)
599{
600 LIMITED_METHOD_CONTRACT;
601
602 uint64_t restricted_limit = GetRestrictedPhysicalMemoryLimit();
603 if (restricted_limit != 0)
604 {
605 size_t workingSetSize;
606 BOOL status = FALSE;
607#ifndef FEATURE_PAL
608 if (!g_UseRestrictedVirtualMemory)
609 {
610 PROCESS_MEMORY_COUNTERS pmc;
611 status = GCGetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc));
612 workingSetSize = pmc.WorkingSetSize;
613 }
614#else
615 status = PAL_GetPhysicalMemoryUsed(&workingSetSize);
616#endif
617 if(status)
618 {
619 if (memory_load)
620 *memory_load = (uint32_t)((float)workingSetSize * 100.0 / (float)restricted_limit);
621 if (available_physical)
622 {
623 if(workingSetSize > restricted_limit)
624 *available_physical = 0;
625 else
626 *available_physical = restricted_limit - workingSetSize;
627 }
628 // Available page file doesn't mean much when physical memory is restricted since
629 // we don't know how much of it is available to this process so we are not going to
630 // bother to make another OS call for it.
631 if (available_page_file)
632 *available_page_file = 0;
633
634 return;
635 }
636 }
637
638 MEMORYSTATUSEX ms;
639 ::GetProcessMemoryLoad(&ms);
640
641#ifndef FEATURE_PAL
642 if (g_UseRestrictedVirtualMemory)
643 {
644 _ASSERTE (ms.ullTotalVirtual == restricted_limit);
645 if (memory_load != NULL)
646 *memory_load = (uint32_t)((float)(ms.ullTotalVirtual - ms.ullAvailVirtual) * 100.0 / (float)ms.ullTotalVirtual);
647 if (available_physical != NULL)
648 *available_physical = ms.ullTotalVirtual;
649
650 // Available page file isn't helpful when we are restricted by virtual memory
651 // since the amount of memory we can reserve is less than the amount of
652 // memory we can commit.
653 if (available_page_file != NULL)
654 *available_page_file = 0;
655 }
656 else
657#endif //!FEATURE_PAL
658 {
659 if (memory_load != NULL)
660 *memory_load = ms.dwMemoryLoad;
661 if (available_physical != NULL)
662 *available_physical = ms.ullAvailPhys;
663 if (available_page_file != NULL)
664 *available_page_file = ms.ullAvailPageFile;
665 }
666}
667
668// Get a high precision performance counter
669// Return:
670// The counter value
671int64_t GCToOSInterface::QueryPerformanceCounter()
672{
673 LIMITED_METHOD_CONTRACT;
674
675 LARGE_INTEGER ts;
676 if (!::QueryPerformanceCounter(&ts))
677 {
678 DebugBreak();
679 _ASSERTE(!"Fatal Error - cannot query performance counter.");
680 EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); // TODO: fatal error
681 }
682
683 return ts.QuadPart;
684}
685
686// Get a frequency of the high precision performance counter
687// Return:
688// The counter frequency
689int64_t GCToOSInterface::QueryPerformanceFrequency()
690{
691 LIMITED_METHOD_CONTRACT;
692
693 LARGE_INTEGER frequency;
694 if (!::QueryPerformanceFrequency(&frequency))
695 {
696 DebugBreak();
697 _ASSERTE(!"Fatal Error - cannot query performance counter.");
698 EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); // TODO: fatal error
699 }
700
701 return frequency.QuadPart;
702}
703
704// Get a time stamp with a low precision
705// Return:
706// Time stamp in milliseconds
707uint32_t GCToOSInterface::GetLowPrecisionTimeStamp()
708{
709 LIMITED_METHOD_CONTRACT;
710
711 return ::GetTickCount();
712}
713
714uint32_t GCToOSInterface::GetTotalProcessorCount()
715{
716 LIMITED_METHOD_CONTRACT;
717
718 if (CPUGroupInfo::CanEnableGCCPUGroups())
719 {
720 return CPUGroupInfo::GetNumActiveProcessors();
721 }
722 else
723 {
724 return g_SystemInfo.dwNumberOfProcessors;
725 }
726}
727
728bool GCToOSInterface::CanEnableGCNumaAware()
729{
730 LIMITED_METHOD_CONTRACT;
731
732 return NumaNodeInfo::CanEnableGCNumaAware() != FALSE;
733}
734
735bool GCToOSInterface::GetNumaProcessorNode(PPROCESSOR_NUMBER proc_no, uint16_t *node_no)
736{
737 LIMITED_METHOD_CONTRACT;
738
739 return NumaNodeInfo::GetNumaProcessorNodeEx(proc_no, node_no) != FALSE;
740}
741
742bool GCToOSInterface::CanEnableGCCPUGroups()
743{
744 LIMITED_METHOD_CONTRACT;
745
746 return CPUGroupInfo::CanEnableGCCPUGroups() != FALSE;
747}
748
749void GCToOSInterface::GetGroupForProcessor(uint16_t processor_number, uint16_t* group_number, uint16_t* group_processor_number)
750{
751 LIMITED_METHOD_CONTRACT;
752
753 return CPUGroupInfo::GetGroupForProcessor(processor_number, group_number, group_processor_number);
754}
755
756// Initialize the critical section
757void CLRCriticalSection::Initialize()
758{
759 WRAPPER_NO_CONTRACT;
760 UnsafeInitializeCriticalSection(&m_cs);
761}
762
763// Destroy the critical section
764void CLRCriticalSection::Destroy()
765{
766 WRAPPER_NO_CONTRACT;
767 UnsafeDeleteCriticalSection(&m_cs);
768}
769
770// Enter the critical section. Blocks until the section can be entered.
771void CLRCriticalSection::Enter()
772{
773 WRAPPER_NO_CONTRACT;
774 UnsafeEnterCriticalSection(&m_cs);
775}
776
777// Leave the critical section
778void CLRCriticalSection::Leave()
779{
780 WRAPPER_NO_CONTRACT;
781 UnsafeLeaveCriticalSection(&m_cs);
782}
783
784// An implementatino of GCEvent that delegates to
785// a CLREvent, which in turn delegates to the PAL. This event
786// is also host-aware.
787class GCEvent::Impl
788{
789private:
790 CLREvent m_event;
791
792public:
793 Impl() = default;
794
795 bool IsValid()
796 {
797 WRAPPER_NO_CONTRACT;
798
799 return !!m_event.IsValid();
800 }
801
802 void CloseEvent()
803 {
804 WRAPPER_NO_CONTRACT;
805
806 assert(m_event.IsValid());
807 m_event.CloseEvent();
808 }
809
810 void Set()
811 {
812 WRAPPER_NO_CONTRACT;
813
814 assert(m_event.IsValid());
815 m_event.Set();
816 }
817
818 void Reset()
819 {
820 WRAPPER_NO_CONTRACT;
821
822 assert(m_event.IsValid());
823 m_event.Reset();
824 }
825
826 uint32_t Wait(uint32_t timeout, bool alertable)
827 {
828 WRAPPER_NO_CONTRACT;
829
830 assert(m_event.IsValid());
831 return m_event.Wait(timeout, alertable);
832 }
833
834 bool CreateAutoEvent(bool initialState)
835 {
836 CONTRACTL {
837 NOTHROW;
838 GC_NOTRIGGER;
839 } CONTRACTL_END;
840
841 return !!m_event.CreateAutoEventNoThrow(initialState);
842 }
843
844 bool CreateManualEvent(bool initialState)
845 {
846 CONTRACTL {
847 NOTHROW;
848 GC_NOTRIGGER;
849 } CONTRACTL_END;
850
851 return !!m_event.CreateManualEventNoThrow(initialState);
852 }
853
854 bool CreateOSAutoEvent(bool initialState)
855 {
856 CONTRACTL {
857 NOTHROW;
858 GC_NOTRIGGER;
859 } CONTRACTL_END;
860
861 return !!m_event.CreateOSAutoEventNoThrow(initialState);
862 }
863
864 bool CreateOSManualEvent(bool initialState)
865 {
866 CONTRACTL {
867 NOTHROW;
868 GC_NOTRIGGER;
869 } CONTRACTL_END;
870
871 return !!m_event.CreateOSManualEventNoThrow(initialState);
872 }
873};
874
875GCEvent::GCEvent()
876 : m_impl(nullptr)
877{
878}
879
880void GCEvent::CloseEvent()
881{
882 WRAPPER_NO_CONTRACT;
883
884 assert(m_impl != nullptr);
885 m_impl->CloseEvent();
886}
887
888void GCEvent::Set()
889{
890 WRAPPER_NO_CONTRACT;
891
892 assert(m_impl != nullptr);
893 m_impl->Set();
894}
895
896void GCEvent::Reset()
897{
898 WRAPPER_NO_CONTRACT;
899
900 assert(m_impl != nullptr);
901 m_impl->Reset();
902}
903
904uint32_t GCEvent::Wait(uint32_t timeout, bool alertable)
905{
906 WRAPPER_NO_CONTRACT;
907
908 assert(m_impl != nullptr);
909 return m_impl->Wait(timeout, alertable);
910}
911
912bool GCEvent::CreateManualEventNoThrow(bool initialState)
913{
914 CONTRACTL {
915 NOTHROW;
916 GC_NOTRIGGER;
917 } CONTRACTL_END;
918
919 assert(m_impl == nullptr);
920 NewHolder<GCEvent::Impl> event = new (nothrow) GCEvent::Impl();
921 if (!event)
922 {
923 return false;
924 }
925
926 event->CreateManualEvent(initialState);
927 m_impl = event.Extract();
928 return true;
929}
930
931bool GCEvent::CreateAutoEventNoThrow(bool initialState)
932{
933 CONTRACTL {
934 NOTHROW;
935 GC_NOTRIGGER;
936 } CONTRACTL_END;
937
938 assert(m_impl == nullptr);
939 NewHolder<GCEvent::Impl> event = new (nothrow) GCEvent::Impl();
940 if (!event)
941 {
942 return false;
943 }
944
945 event->CreateAutoEvent(initialState);
946 m_impl = event.Extract();
947 return IsValid();
948}
949
950bool GCEvent::CreateOSAutoEventNoThrow(bool initialState)
951{
952 CONTRACTL {
953 NOTHROW;
954 GC_NOTRIGGER;
955 } CONTRACTL_END;
956
957 assert(m_impl == nullptr);
958 NewHolder<GCEvent::Impl> event = new (nothrow) GCEvent::Impl();
959 if (!event)
960 {
961 return false;
962 }
963
964 event->CreateOSAutoEvent(initialState);
965 m_impl = event.Extract();
966 return IsValid();
967}
968
969bool GCEvent::CreateOSManualEventNoThrow(bool initialState)
970{
971 CONTRACTL {
972 NOTHROW;
973 GC_NOTRIGGER;
974 } CONTRACTL_END;
975
976 assert(m_impl == nullptr);
977 NewHolder<GCEvent::Impl> event = new (nothrow) GCEvent::Impl();
978 if (!event)
979 {
980 return false;
981 }
982
983 event->CreateOSManualEvent(initialState);
984 m_impl = event.Extract();
985 return IsValid();
986}
987
988