243b8979ed7b5d0e33e9ab1d926fa5bd110adc18
[platform/upstream/coreclr.git] / src / vm / gcenv.os.cpp
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
30 uint32_t g_pageSizeUnixInl = 0;
31 #endif
32
33 static AffinitySet g_processAffinitySet;
34
35 class GroupProcNo
36 {
37     uint16_t m_groupProc;
38
39 public:
40
41     static const uint16_t NoGroup = 0x3ff;
42
43     GroupProcNo(uint16_t groupProc) : m_groupProc(groupProc)
44     {
45     }
46
47     GroupProcNo(uint16_t group, uint16_t procIndex) : m_groupProc((group << 6) | procIndex)
48     {
49         assert(group <= 0x3ff);
50         assert(procIndex <= 0x3f);
51     }
52
53     uint16_t GetGroup() { return m_groupProc >> 6; }
54     uint16_t GetProcIndex() { return m_groupProc & 0x3f; }
55     uint16_t GetCombinedValue() { return m_groupProc; }
56 };
57
58 // Initialize the interface implementation
59 // Return:
60 //  true if it has succeeded, false if it has failed
61 bool GCToOSInterface::Initialize()
62 {
63     LIMITED_METHOD_CONTRACT;
64
65 #ifdef FEATURE_PAL
66     g_pageSizeUnixInl = GetOsPageSize();
67 #endif
68
69     if (CPUGroupInfo::CanEnableGCCPUGroups())
70     {
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++)
74         {
75             g_processAffinitySet.Add(i);
76         }
77     }
78     else
79     {
80         // When CPU groups are disabled, the process affinity mask specified at the process launch cannot be
81         // escaped.
82         uintptr_t pmask, smask;
83         if (!!::GetProcessAffinityMask(::GetCurrentProcess(), (PDWORD_PTR)&pmask, (PDWORD_PTR)&smask))
84         {
85             pmask &= smask;
86
87             for (size_t i = 0; i < 8 * sizeof(uintptr_t); i++)
88             {
89                 if ((pmask & ((uintptr_t)1 << i)) != 0)
90                 {
91                     g_processAffinitySet.Add(i);
92                 }
93             }
94         }
95     }
96
97     return true;
98 }
99
100 // Shutdown the interface implementation
101 void GCToOSInterface::Shutdown()
102 {
103     LIMITED_METHOD_CONTRACT;
104 }
105
106 // Get numeric id of the current thread if possible on the
107 // current platform. It is indended for logging purposes only.
108 // Return:
109 //  Numeric id of the current thread or 0 if the 
110 uint64_t GCToOSInterface::GetCurrentThreadIdForLogging()
111 {
112     LIMITED_METHOD_CONTRACT;
113     return ::GetCurrentThreadId();
114 }
115
116 // Get id of the process
117 // Return:
118 //  Id of the current process
119 uint32_t GCToOSInterface::GetCurrentProcessId()
120 {
121     LIMITED_METHOD_CONTRACT;
122     return ::GetCurrentProcessId();
123 }
124
125 // Set ideal processor for the current thread
126 // Parameters:
127 //  srcProcNo - processor number the thread currently runs on
128 //  dstProcNo - processor number the thread should be migrated to
129 // Return:
130 //  true if it has succeeded, false if it has failed
131 bool GCToOSInterface::SetCurrentThreadIdealAffinity(uint16_t srcProcNo, uint16_t dstProcNo)
132 {
133     LIMITED_METHOD_CONTRACT;
134
135     bool success = true;
136
137     GroupProcNo srcGroupProcNo(srcProcNo);
138     GroupProcNo dstGroupProcNo(dstProcNo);
139
140     if (CPUGroupInfo::CanEnableGCCPUGroups())
141     {
142         if (srcGroupProcNo.GetGroup() != dstGroupProcNo.GetGroup())
143         {
144             //only set ideal processor when srcProcNo and dstProcNo are in the same cpu
145             //group. DO NOT MOVE THREADS ACROSS CPU GROUPS
146             return true;
147         }
148     }
149
150 #if !defined(FEATURE_CORESYSTEM)
151     SetThreadIdealProcessor(GetCurrentThread(), (DWORD)dstGroupProcNo.GetProcIndex());
152 #else
153     PROCESSOR_NUMBER proc;
154
155     if (dstGroupProcNo.GetGroup() != GroupProcNo::NoGroup)
156     {
157         proc.Group = (WORD)dstGroupProcNo.GetGroup();
158         proc.Number = (BYTE)dstGroupProcNo.GetProcIndex();
159         proc.Reserved = 0;
160
161         success = !!SetThreadIdealProcessorEx(GetCurrentThread(), &proc, NULL);
162     }
163 #if !defined(FEATURE_PAL)
164     else
165     {
166         if (GetThreadIdealProcessorEx(GetCurrentThread(), &proc))
167         {
168             proc.Number = (BYTE)dstGroupProcNo.GetProcIndex();
169             success = !!SetThreadIdealProcessorEx(GetCurrentThread(), &proc, &proc);
170         }
171     }
172 #endif // !defined(FEATURE_PAL)
173 #endif
174
175     return success;
176 }
177
178 // Get the number of the current processor
179 uint32_t GCToOSInterface::GetCurrentProcessorNumber()
180 {
181     LIMITED_METHOD_CONTRACT;
182
183     _ASSERTE(CanGetCurrentProcessorNumber());
184     return ::GetCurrentProcessorNumber();
185 }
186
187 // Check if the OS supports getting current processor number
188 bool GCToOSInterface::CanGetCurrentProcessorNumber()
189 {
190     LIMITED_METHOD_CONTRACT;
191
192 #ifdef FEATURE_PAL
193     return PAL_HasGetCurrentProcessorNumber();
194 #else
195     // on all Windows platforms we support this API exists
196     return true;
197 #endif
198 }
199
200 // Flush write buffers of processors that are executing threads of the current process
201 void GCToOSInterface::FlushProcessWriteBuffers()
202 {
203     LIMITED_METHOD_CONTRACT;
204     ::FlushProcessWriteBuffers();
205 }
206
207 // Break into a debugger
208 void GCToOSInterface::DebugBreak()
209 {
210     LIMITED_METHOD_CONTRACT;
211     ::DebugBreak();
212 }
213
214 // Causes the calling thread to sleep for the specified number of milliseconds
215 // Parameters:
216 //  sleepMSec   - time to sleep before switching to another thread
217 void GCToOSInterface::Sleep(uint32_t sleepMSec)
218 {
219     LIMITED_METHOD_CONTRACT;
220     __SwitchToThread(sleepMSec, 0);
221 }
222
223 // Causes the calling thread to yield execution to another thread that is ready to run on the current processor.
224 // Parameters:
225 //  switchCount - number of times the YieldThread was called in a loop
226 void GCToOSInterface::YieldThread(uint32_t switchCount)
227 {
228     LIMITED_METHOD_CONTRACT;
229     __SwitchToThread(0, switchCount);
230 }
231
232 // Reserve virtual memory range.
233 // Parameters:
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
238 // Return:
239 //  Starting virtual address of the reserved range
240 void* GCToOSInterface::VirtualReserve(size_t size, size_t alignment, uint32_t flags)
241 {
242     LIMITED_METHOD_CONTRACT;
243
244     DWORD memFlags = (flags & VirtualReserveFlags::WriteWatch) ? (MEM_RESERVE | MEM_WRITE_WATCH) : MEM_RESERVE;
245
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);
250     if (alignment == 0)
251     {
252         return ::ClrVirtualAlloc(0, aligned_size, memFlags, PAGE_READWRITE);
253     }
254     else
255     {
256         return ::ClrVirtualAllocAligned(0, aligned_size, memFlags, PAGE_READWRITE, alignment);
257     }
258 }
259
260 // Release virtual memory range previously reserved using VirtualReserve
261 // Parameters:
262 //  address - starting virtual address
263 //  size    - size of the virtual memory range
264 // Return:
265 //  true if it has succeeded, false if it has failed
266 bool GCToOSInterface::VirtualRelease(void* address, size_t size)
267 {
268     LIMITED_METHOD_CONTRACT;
269
270     UNREFERENCED_PARAMETER(size);
271     return !!::ClrVirtualFree(address, 0, MEM_RELEASE);
272 }
273
274 // Commit virtual memory range. It must be part of a range reserved using VirtualReserve.
275 // Parameters:
276 //  address - starting virtual address
277 //  size    - size of the virtual memory range
278 // Return:
279 //  true if it has succeeded, false if it has failed
280 bool GCToOSInterface::VirtualCommit(void* address, size_t size, uint16_t node)
281 {
282     LIMITED_METHOD_CONTRACT;
283
284     if (node == NUMA_NODE_UNDEFINED)
285     {
286         return ::ClrVirtualAlloc(address, size, MEM_COMMIT, PAGE_READWRITE) != NULL;
287     }
288     else
289     {
290         return NumaNodeInfo::VirtualAllocExNuma(::GetCurrentProcess(), address, size, MEM_COMMIT, PAGE_READWRITE, node) != NULL;
291     }
292 }
293
294 // Decomit virtual memory range.
295 // Parameters:
296 //  address - starting virtual address
297 //  size    - size of the virtual memory range
298 // Return:
299 //  true if it has succeeded, false if it has failed
300 bool GCToOSInterface::VirtualDecommit(void* address, size_t size)
301 {
302     LIMITED_METHOD_CONTRACT;
303
304     return !!::ClrVirtualFree(address, size, MEM_DECOMMIT);
305 }
306
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.
309 // Parameters:
310 //  address - starting virtual address
311 //  size    - size of the virtual memory range
312 //  unlock  - true if the memory range should also be unlocked
313 // Return:
314 //  true if it has succeeded, false if it has failed
315 bool GCToOSInterface::VirtualReset(void * address, size_t size, bool unlock)
316 {
317     LIMITED_METHOD_CONTRACT;
318
319     bool success = ::ClrVirtualAlloc(address, size, MEM_RESET, PAGE_READWRITE) != NULL;
320 #ifndef FEATURE_PAL
321     if (success && unlock)
322     {
323         // Remove the page range from the working set
324         ::VirtualUnlock(address, size);
325     }
326 #endif // FEATURE_PAL
327
328     return success;
329 }
330
331 // Check if the OS supports write watching
332 bool GCToOSInterface::SupportsWriteWatch()
333 {
334     LIMITED_METHOD_CONTRACT;
335
336     bool writeWatchSupported = false;
337
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);
342     if (mem != NULL)
343     {
344         VirtualRelease (mem, g_SystemInfo.dwAllocationGranularity);
345         writeWatchSupported = true;
346     }
347
348     return writeWatchSupported;
349 }
350
351 // Reset the write tracking state for the specified virtual memory range.
352 // Parameters:
353 //  address - starting virtual address
354 //  size    - size of the virtual memory range
355 void GCToOSInterface::ResetWriteWatch(void* address, size_t size)
356 {
357     LIMITED_METHOD_CONTRACT;
358
359     ::ResetWriteWatch(address, size);
360 }
361
362 // Retrieve addresses of the pages that are written to in a region of virtual memory
363 // Parameters:
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.
370 // Return:
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)
373 {
374     LIMITED_METHOD_CONTRACT;
375
376     uint32_t flags = resetState ? 1 : 0;
377     ULONG granularity;
378
379     bool success = ::GetWriteWatch(flags, address, size, pageAddresses, (ULONG_PTR*)pageAddressesCount, &granularity) == 0;
380     _ASSERTE (granularity == GetOsPageSize());
381
382     return success;
383 }
384
385 // Get size of the largest cache on the processor die
386 // Parameters:
387 //  trueSize - true to return true cache size, false to return scaled up size based on
388 //             the processor architecture
389 // Return:
390 //  Size of the cache
391 size_t GCToOSInterface::GetCacheSizePerLogicalCpu(bool trueSize)
392 {
393     LIMITED_METHOD_CONTRACT;
394
395     return ::GetCacheSizePerLogicalCpu(trueSize);
396 }
397
398 // Sets the calling thread's affinity to only run on the processor specified
399 // Parameters:
400 //  procNo - The requested processor for the calling thread.
401 // Return:
402 //  true if setting the affinity was successful, false otherwise.
403 bool GCToOSInterface::SetThreadAffinity(uint16_t procNo)
404 {
405     LIMITED_METHOD_CONTRACT;
406
407     GroupProcNo groupProcNo(procNo);
408
409     if (groupProcNo.GetGroup() != GroupProcNo::NoGroup)
410     {
411         GROUP_AFFINITY ga;
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
415         ga.Reserved[2] = 0;
416         ga.Mask = (size_t)1 << groupProcNo.GetProcIndex();
417         return !!SetThreadGroupAffinity(GetCurrentThread(), &ga, nullptr);
418     }
419     else
420     {
421         return !!SetThreadAffinityMask(GetCurrentThread(), (DWORD_PTR)1 << groupProcNo.GetProcIndex());
422     }
423 }
424
425 // Boosts the calling thread's thread priority to a level higher than the default
426 // for new threads.
427 // Parameters:
428 //  None.
429 // Return:
430 //  true if the priority boost was successful, false otherwise.
431 bool GCToOSInterface::BoostThreadPriority()
432 {
433     return !!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
434 }
435
436 // Set the set of processors enabled for GC threads for the current process based on config specified affinity mask and set
437 // Parameters:
438 //  configAffinityMask - mask specified by the GCHeapAffinitizeMask config
439 //  configAffinitySet  - affinity set specified by the GCHeapAffinitizeRanges config
440 // Return:
441 //  set of enabled processors
442 const AffinitySet* GCToOSInterface::SetGCThreadsAffinitySet(uintptr_t configAffinityMask, const AffinitySet* configAffinitySet)
443 {
444     if (CPUGroupInfo::CanEnableGCCPUGroups())
445     {
446         if (!configAffinitySet->IsEmpty())
447         {
448             // Update the process affinity set using the configured set
449             for (size_t i = 0; i < MAX_SUPPORTED_CPUS; i++)
450             {
451                 if (g_processAffinitySet.Contains(i) && !configAffinitySet->Contains(i))
452                 {
453                     g_processAffinitySet.Remove(i);
454                 }
455             }
456         }
457     }
458     else
459     {
460         if (configAffinityMask != 0)
461         {
462             // Update the process affinity set using the configured mask
463             for (size_t i = 0; i < 8 * sizeof(uintptr_t); i++)
464             {
465                 if (g_processAffinitySet.Contains(i) && ((configAffinityMask & ((uintptr_t)1 << i)) == 0))
466                 {
467                     g_processAffinitySet.Remove(i);
468                 }
469             }
470         }
471     }
472
473     return &g_processAffinitySet;
474 }
475
476 // Get number of processors assigned to the current process
477 // Return:
478 //  The number of processors
479 uint32_t GCToOSInterface::GetCurrentProcessCpuCount()
480 {
481     LIMITED_METHOD_CONTRACT;
482
483     // GetCurrentProcessCpuCount only returns up to 64 procs.
484     return CPUGroupInfo::CanEnableGCCPUGroups() ?
485                 GCToOSInterface::GetTotalProcessorCount():
486                 ::GetCurrentProcessCpuCount();
487 }
488
489 // Return the size of the user-mode portion of the virtual address space of this process.
490 // Return:
491 //  non zero if it has succeeded, 0 if it has failed
492 size_t GCToOSInterface::GetVirtualMemoryLimit()
493 {
494     LIMITED_METHOD_CONTRACT;
495
496     MEMORYSTATUSEX memStatus;
497     ::GetProcessMemoryLoad(&memStatus);
498
499     return (size_t)memStatus.ullTotalVirtual;
500 }
501
502 static size_t g_RestrictedPhysicalMemoryLimit = (size_t)MAX_PTR;
503
504 #ifndef FEATURE_PAL
505
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;
509
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;
512
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);
515
516 static size_t GetRestrictedPhysicalMemoryLimit()
517 {
518     LIMITED_METHOD_CONTRACT;
519
520     // The limit was cached already
521     if (g_RestrictedPhysicalMemoryLimit != (size_t)MAX_PTR)
522         return g_RestrictedPhysicalMemoryLimit;
523
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;
529
530     PIS_PROCESS_IN_JOB GCIsProcessInJob = 0;
531     PQUERY_INFORMATION_JOB_OBJECT GCQueryInformationJobObject = 0;
532
533     GCIsProcessInJob = &(::IsProcessInJob);
534
535     if (!GCIsProcessInJob(GetCurrentProcess(), NULL, &in_job_p))
536         goto exit;
537
538     if (in_job_p)
539     {
540         hinstKernel32 = WszLoadLibrary(L"kernel32.dll");
541         if (!hinstKernel32)
542             goto exit;
543
544         GCGetProcessMemoryInfo = (PGET_PROCESS_MEMORY_INFO)GetProcAddress(hinstKernel32, "K32GetProcessMemoryInfo");
545
546         if (!GCGetProcessMemoryInfo)
547             goto exit;
548
549         GCQueryInformationJobObject = &(::QueryInformationJobObject);
550
551         if (!GCQueryInformationJobObject)
552             goto exit;
553
554         JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info;
555         if (GCQueryInformationJobObject (NULL, JobObjectExtendedLimitInformation, &limit_info, 
556             sizeof(limit_info), NULL))
557         {
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;
561
562             // Notes on the NT job object:
563             //
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.
569             // 
570             // The commit limit is already reflected by default when you run in a 
571             // job but the physical memory load is not.
572             //
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;
579
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))
583             {
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);
586
587                 MEMORYSTATUSEX ms;
588                 ::GetProcessMemoryLoad(&ms);
589                 total_virtual = ms.ullTotalVirtual;
590                 total_physical = ms.ullAvailPhys;
591
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);
594             }
595         }
596     }
597
598 exit:
599     if (job_physical_memory_limit == (size_t)MAX_PTR)
600     {
601         job_physical_memory_limit = 0;
602
603         if (hinstKernel32 != 0)
604         {
605             FreeLibrary(hinstKernel32);
606             hinstKernel32 = 0;
607             GCGetProcessMemoryInfo = 0;
608         }
609     }
610
611     // Check to see if we are limited by VM.
612     if (total_virtual == 0)
613     {
614         MEMORYSTATUSEX ms;
615         ::GetProcessMemoryLoad(&ms);
616
617         total_virtual = ms.ullTotalVirtual;
618         total_physical = ms.ullTotalPhys;
619     }
620
621     if (job_physical_memory_limit != 0)
622     {
623         total_physical = job_physical_memory_limit;
624     }
625
626     if (total_virtual < total_physical)
627     {
628         if (hinstKernel32 != 0)
629         {
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;
634         }
635         g_UseRestrictedVirtualMemory = true;
636         job_physical_memory_limit = (size_t)total_virtual;
637     }
638
639     VolatileStore(&g_RestrictedPhysicalMemoryLimit, job_physical_memory_limit);
640     return g_RestrictedPhysicalMemoryLimit;
641 }
642
643 #else
644
645 static size_t GetRestrictedPhysicalMemoryLimit()
646 {
647     LIMITED_METHOD_CONTRACT;
648
649     // The limit was cached already
650     if (g_RestrictedPhysicalMemoryLimit != (size_t)MAX_PTR)
651         return g_RestrictedPhysicalMemoryLimit;
652
653     size_t memory_limit = PAL_GetRestrictedPhysicalMemoryLimit();
654     
655     VolatileStore(&g_RestrictedPhysicalMemoryLimit, memory_limit);
656     return g_RestrictedPhysicalMemoryLimit;
657 }
658 #endif // FEATURE_PAL
659
660
661 // Get the physical memory that this process can use.
662 // Return:
663 //  non zero if it has succeeded, 0 if it has failed
664 //
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)
668 {
669     LIMITED_METHOD_CONTRACT;
670
671     if (is_restricted)
672         *is_restricted = false;
673
674     size_t restricted_limit = GetRestrictedPhysicalMemoryLimit();
675     if (restricted_limit != 0)
676     {
677         if (is_restricted 
678 #ifndef FEATURE_PAL
679             && !g_UseRestrictedVirtualMemory
680 #endif
681             )
682             *is_restricted = true;
683
684         return restricted_limit;
685     }
686
687     MEMORYSTATUSEX memStatus;
688     ::GetProcessMemoryLoad(&memStatus);
689
690     return memStatus.ullTotalPhys;
691 }
692
693 // Get memory status
694 // Parameters:
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.
699 // Remarks:
700 //  Any parameter can be null.
701 void GCToOSInterface::GetMemoryStatus(uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file)
702 {
703     LIMITED_METHOD_CONTRACT;
704
705     uint64_t restricted_limit = GetRestrictedPhysicalMemoryLimit();
706     if (restricted_limit != 0)
707     {
708         size_t workingSetSize;
709         BOOL status = FALSE;
710 #ifndef FEATURE_PAL
711         if (!g_UseRestrictedVirtualMemory)
712         {
713             PROCESS_MEMORY_COUNTERS pmc;
714             status = GCGetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc));
715             workingSetSize = pmc.WorkingSetSize;
716         }
717 #else
718         status = PAL_GetPhysicalMemoryUsed(&workingSetSize);
719 #endif
720         if(status)
721         {
722             if (memory_load)
723                 *memory_load = (uint32_t)((float)workingSetSize * 100.0 / (float)restricted_limit);
724             if (available_physical)
725             {
726                 if(workingSetSize > restricted_limit)
727                     *available_physical = 0;
728                 else
729                     *available_physical = restricted_limit - workingSetSize;
730             }
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;
736
737             return;
738         }
739     }
740
741     MEMORYSTATUSEX ms;
742     ::GetProcessMemoryLoad(&ms);
743     
744 #ifndef FEATURE_PAL
745     if (g_UseRestrictedVirtualMemory)
746     {
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;
752
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;
758     }
759     else
760 #endif //!FEATURE_PAL
761     {
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;
768     }
769 }
770
771 // Get a high precision performance counter
772 // Return:
773 //  The counter value
774 int64_t GCToOSInterface::QueryPerformanceCounter()
775 {
776     LIMITED_METHOD_CONTRACT;
777
778     LARGE_INTEGER ts;
779     if (!::QueryPerformanceCounter(&ts))
780     {
781         DebugBreak();
782         _ASSERTE(!"Fatal Error - cannot query performance counter.");
783         EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);        // TODO: fatal error        
784     }
785
786     return ts.QuadPart;
787 }
788
789 // Get a frequency of the high precision performance counter
790 // Return:
791 //  The counter frequency
792 int64_t GCToOSInterface::QueryPerformanceFrequency()
793 {
794     LIMITED_METHOD_CONTRACT;
795
796     LARGE_INTEGER frequency;
797     if (!::QueryPerformanceFrequency(&frequency))
798     {
799         DebugBreak();
800         _ASSERTE(!"Fatal Error - cannot query performance counter.");
801         EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);        // TODO: fatal error        
802     }
803
804     return frequency.QuadPart;
805 }
806
807 // Get a time stamp with a low precision
808 // Return:
809 //  Time stamp in milliseconds
810 uint32_t GCToOSInterface::GetLowPrecisionTimeStamp()
811 {
812     LIMITED_METHOD_CONTRACT;
813
814     return ::GetTickCount();
815 }
816
817 uint32_t GCToOSInterface::GetTotalProcessorCount()
818 {
819     LIMITED_METHOD_CONTRACT;
820
821     if (CPUGroupInfo::CanEnableGCCPUGroups())
822     {
823         return CPUGroupInfo::GetNumActiveProcessors();
824     }
825     else
826     {
827         return g_SystemInfo.dwNumberOfProcessors;
828     }
829 }
830
831 bool GCToOSInterface::CanEnableGCNumaAware()
832 {
833     LIMITED_METHOD_CONTRACT;
834
835     return NumaNodeInfo::CanEnableGCNumaAware() != FALSE;
836 }
837
838 bool GCToOSInterface::GetNumaProcessorNode(uint16_t proc_no, uint16_t *node_no)
839 {
840     LIMITED_METHOD_CONTRACT;
841
842     GroupProcNo groupProcNo(proc_no);
843
844     PROCESSOR_NUMBER procNumber;
845     procNumber.Group    = groupProcNo.GetGroup();
846     procNumber.Number   = (BYTE)groupProcNo.GetProcIndex();
847     procNumber.Reserved = 0;
848
849     return NumaNodeInfo::GetNumaProcessorNodeEx(&procNumber, node_no) != FALSE;
850 }
851
852 // Get processor number and optionally its NUMA node number for the specified heap number
853 // Parameters:
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
857 // Return:
858 //  true if it succeeded
859 bool GCToOSInterface::GetProcessorForHeap(uint16_t heap_number, uint16_t* proc_no, uint16_t* node_no)
860 {
861     bool success = false;
862
863     if (CPUGroupInfo::CanEnableGCCPUGroups())
864     {
865         uint16_t gn, gpn;
866         CPUGroupInfo::GetGroupForProcessor((uint16_t)heap_number, &gn, &gpn);
867
868         *proc_no = GroupProcNo(gn, gpn).GetCombinedValue();
869         if (GCToOSInterface::CanEnableGCNumaAware())
870         {
871             if (!GCToOSInterface::GetNumaProcessorNode(*proc_no, node_no))
872             {
873                 *node_no = NUMA_NODE_UNDEFINED;
874             }
875         }
876         else
877         {   // no numa setting, each cpu group is treated as a node
878             *node_no = gn;
879         }
880
881         success = true;
882     }
883     else
884     {
885         int bit_number = 0;
886         uint8_t proc_number = 0;
887         for (uintptr_t mask = 1; mask != 0; mask <<= 1)
888         {
889             if (g_processAffinitySet.Contains(proc_number))
890             {
891                 if (bit_number == heap_number)
892                 {
893                     *proc_no = GroupProcNo(GroupProcNo::NoGroup, proc_number).GetCombinedValue();
894
895                     if (GCToOSInterface::CanEnableGCNumaAware())
896                     {
897                         if (!GCToOSInterface::GetNumaProcessorNode(proc_number, node_no))
898                         {
899                             *node_no = NUMA_NODE_UNDEFINED;
900                         }
901                     }
902
903                     success = true;
904                     break;
905                 }
906                 bit_number++;
907             }
908             proc_number++;
909         }
910     }
911
912     return success;
913 }
914
915 // Initialize the critical section
916 void CLRCriticalSection::Initialize()
917 {
918     WRAPPER_NO_CONTRACT;
919     UnsafeInitializeCriticalSection(&m_cs);
920 }
921
922 // Destroy the critical section
923 void CLRCriticalSection::Destroy()
924 {
925     WRAPPER_NO_CONTRACT;
926     UnsafeDeleteCriticalSection(&m_cs);
927 }
928
929 // Enter the critical section. Blocks until the section can be entered.
930 void CLRCriticalSection::Enter()
931 {
932     WRAPPER_NO_CONTRACT;
933     UnsafeEnterCriticalSection(&m_cs);
934 }
935
936 // Leave the critical section
937 void CLRCriticalSection::Leave()
938 {
939     WRAPPER_NO_CONTRACT;
940     UnsafeLeaveCriticalSection(&m_cs);
941 }
942
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.
946 class GCEvent::Impl
947 {
948 private:
949     CLREvent m_event;
950
951 public:
952     Impl() = default;
953
954     bool IsValid()
955     {
956         WRAPPER_NO_CONTRACT;
957
958         return !!m_event.IsValid();
959     }
960
961     void CloseEvent()
962     {
963         WRAPPER_NO_CONTRACT;
964
965         assert(m_event.IsValid());
966         m_event.CloseEvent();
967     }
968
969     void Set()
970     {
971         WRAPPER_NO_CONTRACT;
972
973         assert(m_event.IsValid());
974         m_event.Set();
975     }
976
977     void Reset()
978     {
979         WRAPPER_NO_CONTRACT;
980
981         assert(m_event.IsValid());
982         m_event.Reset();
983     }
984
985     uint32_t Wait(uint32_t timeout, bool alertable)
986     {
987         WRAPPER_NO_CONTRACT;
988
989         assert(m_event.IsValid());
990         return m_event.Wait(timeout, alertable);
991     }
992
993     bool CreateAutoEvent(bool initialState)
994     {
995         CONTRACTL {
996             NOTHROW;
997             GC_NOTRIGGER;
998         } CONTRACTL_END;
999
1000         return !!m_event.CreateAutoEventNoThrow(initialState);
1001     }
1002
1003     bool CreateManualEvent(bool initialState)
1004     {
1005         CONTRACTL {
1006             NOTHROW;
1007             GC_NOTRIGGER;
1008         } CONTRACTL_END;
1009
1010         return !!m_event.CreateManualEventNoThrow(initialState);
1011     }
1012
1013     bool CreateOSAutoEvent(bool initialState)
1014     {
1015         CONTRACTL {
1016             NOTHROW;
1017             GC_NOTRIGGER;
1018         } CONTRACTL_END;
1019
1020         return !!m_event.CreateOSAutoEventNoThrow(initialState);
1021     }
1022
1023     bool CreateOSManualEvent(bool initialState)
1024     {
1025         CONTRACTL {
1026             NOTHROW;
1027             GC_NOTRIGGER;
1028         } CONTRACTL_END;
1029
1030         return !!m_event.CreateOSManualEventNoThrow(initialState);
1031     }
1032 };
1033
1034 GCEvent::GCEvent()
1035   : m_impl(nullptr)
1036 {
1037 }
1038
1039 void GCEvent::CloseEvent()
1040 {
1041     WRAPPER_NO_CONTRACT;
1042
1043     assert(m_impl != nullptr);
1044     m_impl->CloseEvent();
1045 }
1046
1047 void GCEvent::Set()
1048 {
1049     WRAPPER_NO_CONTRACT;
1050
1051     assert(m_impl != nullptr);
1052     m_impl->Set();
1053 }
1054
1055 void GCEvent::Reset()
1056 {
1057     WRAPPER_NO_CONTRACT;
1058
1059     assert(m_impl != nullptr);
1060     m_impl->Reset();
1061 }
1062
1063 uint32_t GCEvent::Wait(uint32_t timeout, bool alertable)
1064 {
1065     WRAPPER_NO_CONTRACT;
1066
1067     assert(m_impl != nullptr);
1068     return m_impl->Wait(timeout, alertable);
1069 }
1070
1071 bool GCEvent::CreateManualEventNoThrow(bool initialState)
1072 {
1073     CONTRACTL {
1074       NOTHROW;
1075       GC_NOTRIGGER;
1076     } CONTRACTL_END;
1077
1078     assert(m_impl == nullptr);
1079     NewHolder<GCEvent::Impl> event = new (nothrow) GCEvent::Impl();
1080     if (!event)
1081     {
1082         return false;
1083     }
1084
1085     event->CreateManualEvent(initialState);
1086     m_impl = event.Extract();
1087     return true;
1088 }
1089
1090 bool GCEvent::CreateAutoEventNoThrow(bool initialState)
1091 {
1092     CONTRACTL {
1093       NOTHROW;
1094       GC_NOTRIGGER;
1095     } CONTRACTL_END;
1096
1097     assert(m_impl == nullptr);
1098     NewHolder<GCEvent::Impl> event = new (nothrow) GCEvent::Impl();
1099     if (!event)
1100     {
1101         return false;
1102     }
1103
1104     event->CreateAutoEvent(initialState);
1105     m_impl = event.Extract();
1106     return IsValid();
1107 }
1108
1109 bool GCEvent::CreateOSAutoEventNoThrow(bool initialState)
1110 {
1111     CONTRACTL {
1112       NOTHROW;
1113       GC_NOTRIGGER;
1114     } CONTRACTL_END;
1115
1116     assert(m_impl == nullptr);
1117     NewHolder<GCEvent::Impl> event = new (nothrow) GCEvent::Impl();
1118     if (!event)
1119     {
1120         return false;
1121     }
1122
1123     event->CreateOSAutoEvent(initialState);
1124     m_impl = event.Extract();
1125     return IsValid();
1126 }
1127
1128 bool GCEvent::CreateOSManualEventNoThrow(bool initialState)
1129 {
1130     CONTRACTL {
1131       NOTHROW;
1132       GC_NOTRIGGER;
1133     } CONTRACTL_END;
1134
1135     assert(m_impl == nullptr);
1136     NewHolder<GCEvent::Impl> event = new (nothrow) GCEvent::Impl();
1137     if (!event)
1138     {
1139         return false;
1140     }
1141
1142     event->CreateOSManualEvent(initialState);
1143     m_impl = event.Extract();
1144     return IsValid();
1145 }
1146