a56215a2078453b7271463dead12f9f4c580b6fe
[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 #if !defined(FEATURE_PAL)
59
60 static bool g_SeLockMemoryPrivilegeAcquired = false;
61
62 bool InitLargePagesPrivilege()
63 {
64     TOKEN_PRIVILEGES tp;
65     LUID luid;
66     if (!LookupPrivilegeValueW(nullptr, SE_LOCK_MEMORY_NAME, &luid))
67     {
68         return false;
69     }
70
71     tp.PrivilegeCount = 1;
72     tp.Privileges[0].Luid = luid;
73     tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
74
75     HANDLE token;
76     if (!OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token))
77     {
78         return false;
79     }
80
81     BOOL retVal = AdjustTokenPrivileges(token, FALSE, &tp, 0, nullptr, 0);
82     DWORD gls = GetLastError();
83     CloseHandle(token);
84
85     if (!retVal)
86     {
87         return false;
88     }
89
90     if (gls != 0)
91     {
92         return false;
93     }
94
95     return true;
96 }
97
98 #endif // FEATURE_PAL
99
100 // Initialize the interface implementation
101 // Return:
102 //  true if it has succeeded, false if it has failed
103 bool GCToOSInterface::Initialize()
104 {
105     LIMITED_METHOD_CONTRACT;
106
107 #ifdef FEATURE_PAL
108     g_pageSizeUnixInl = GetOsPageSize();
109 #endif
110
111     if (CPUGroupInfo::CanEnableGCCPUGroups())
112     {
113         // When CPU groups are enabled, then the process is not bound by the process affinity set at process launch.
114         // Set the initial affinity mask so that all processors are enabled.
115         for (size_t i = 0; i < CPUGroupInfo::GetNumActiveProcessors(); i++)
116         {
117             g_processAffinitySet.Add(i);
118         }
119     }
120     else
121     {
122         // When CPU groups are disabled, the process affinity mask specified at the process launch cannot be
123         // escaped.
124         uintptr_t pmask, smask;
125         if (!!::GetProcessAffinityMask(::GetCurrentProcess(), (PDWORD_PTR)&pmask, (PDWORD_PTR)&smask))
126         {
127             pmask &= smask;
128
129             for (size_t i = 0; i < 8 * sizeof(uintptr_t); i++)
130             {
131                 if ((pmask & ((uintptr_t)1 << i)) != 0)
132                 {
133                     g_processAffinitySet.Add(i);
134                 }
135             }
136         }
137     }
138
139     return true;
140 }
141
142 // Shutdown the interface implementation
143 void GCToOSInterface::Shutdown()
144 {
145     LIMITED_METHOD_CONTRACT;
146 }
147
148 // Get numeric id of the current thread if possible on the
149 // current platform. It is indended for logging purposes only.
150 // Return:
151 //  Numeric id of the current thread or 0 if the 
152 uint64_t GCToOSInterface::GetCurrentThreadIdForLogging()
153 {
154     LIMITED_METHOD_CONTRACT;
155     return ::GetCurrentThreadId();
156 }
157
158 // Get id of the process
159 // Return:
160 //  Id of the current process
161 uint32_t GCToOSInterface::GetCurrentProcessId()
162 {
163     LIMITED_METHOD_CONTRACT;
164     return ::GetCurrentProcessId();
165 }
166
167 // Set ideal processor for the current thread
168 // Parameters:
169 //  srcProcNo - processor number the thread currently runs on
170 //  dstProcNo - processor number the thread should be migrated to
171 // Return:
172 //  true if it has succeeded, false if it has failed
173 bool GCToOSInterface::SetCurrentThreadIdealAffinity(uint16_t srcProcNo, uint16_t dstProcNo)
174 {
175     LIMITED_METHOD_CONTRACT;
176
177     bool success = true;
178
179     GroupProcNo srcGroupProcNo(srcProcNo);
180     GroupProcNo dstGroupProcNo(dstProcNo);
181
182     if (CPUGroupInfo::CanEnableGCCPUGroups())
183     {
184         if (srcGroupProcNo.GetGroup() != dstGroupProcNo.GetGroup())
185         {
186             //only set ideal processor when srcProcNo and dstProcNo are in the same cpu
187             //group. DO NOT MOVE THREADS ACROSS CPU GROUPS
188             return true;
189         }
190     }
191
192 #if !defined(FEATURE_CORESYSTEM)
193     SetThreadIdealProcessor(GetCurrentThread(), (DWORD)dstGroupProcNo.GetProcIndex());
194 #else
195     PROCESSOR_NUMBER proc;
196
197     if (dstGroupProcNo.GetGroup() != GroupProcNo::NoGroup)
198     {
199         proc.Group = (WORD)dstGroupProcNo.GetGroup();
200         proc.Number = (BYTE)dstGroupProcNo.GetProcIndex();
201         proc.Reserved = 0;
202
203         success = !!SetThreadIdealProcessorEx(GetCurrentThread(), &proc, NULL);
204     }
205 #if !defined(FEATURE_PAL)
206     else
207     {
208         if (GetThreadIdealProcessorEx(GetCurrentThread(), &proc))
209         {
210             proc.Number = (BYTE)dstGroupProcNo.GetProcIndex();
211             success = !!SetThreadIdealProcessorEx(GetCurrentThread(), &proc, &proc);
212         }
213     }
214 #endif // !defined(FEATURE_PAL)
215 #endif
216
217     return success;
218 }
219
220 // Get the number of the current processor
221 uint32_t GCToOSInterface::GetCurrentProcessorNumber()
222 {
223     LIMITED_METHOD_CONTRACT;
224
225     _ASSERTE(CanGetCurrentProcessorNumber());
226     return ::GetCurrentProcessorNumber();
227 }
228
229 // Check if the OS supports getting current processor number
230 bool GCToOSInterface::CanGetCurrentProcessorNumber()
231 {
232     LIMITED_METHOD_CONTRACT;
233
234 #ifdef FEATURE_PAL
235     return PAL_HasGetCurrentProcessorNumber();
236 #else
237     // on all Windows platforms we support this API exists
238     return true;
239 #endif
240 }
241
242 // Flush write buffers of processors that are executing threads of the current process
243 void GCToOSInterface::FlushProcessWriteBuffers()
244 {
245     LIMITED_METHOD_CONTRACT;
246     ::FlushProcessWriteBuffers();
247 }
248
249 // Break into a debugger
250 void GCToOSInterface::DebugBreak()
251 {
252     LIMITED_METHOD_CONTRACT;
253     ::DebugBreak();
254 }
255
256 // Causes the calling thread to sleep for the specified number of milliseconds
257 // Parameters:
258 //  sleepMSec   - time to sleep before switching to another thread
259 void GCToOSInterface::Sleep(uint32_t sleepMSec)
260 {
261     LIMITED_METHOD_CONTRACT;
262     __SwitchToThread(sleepMSec, 0);
263 }
264
265 // Causes the calling thread to yield execution to another thread that is ready to run on the current processor.
266 // Parameters:
267 //  switchCount - number of times the YieldThread was called in a loop
268 void GCToOSInterface::YieldThread(uint32_t switchCount)
269 {
270     LIMITED_METHOD_CONTRACT;
271     __SwitchToThread(0, switchCount);
272 }
273
274 // Reserve virtual memory range.
275 // Parameters:
276 //  address   - starting virtual address, it can be NULL to let the function choose the starting address
277 //  size      - size of the virtual memory range
278 //  alignment - requested memory alignment
279 //  flags     - flags to control special settings like write watching
280 // Return:
281 //  Starting virtual address of the reserved range
282 void* GCToOSInterface::VirtualReserve(size_t size, size_t alignment, uint32_t flags)
283 {
284     LIMITED_METHOD_CONTRACT;
285
286     DWORD memFlags = (flags & VirtualReserveFlags::WriteWatch) ? (MEM_RESERVE | MEM_WRITE_WATCH) : MEM_RESERVE;
287
288     // This is not strictly necessary for a correctness standpoint. Windows already guarantees
289     // allocation granularity alignment when using MEM_RESERVE, so aligning the size here has no effect.
290     // However, ClrVirtualAlloc does expect the size to be aligned to the allocation granularity.
291     size_t aligned_size = (size + g_SystemInfo.dwAllocationGranularity - 1) & ~static_cast<size_t>(g_SystemInfo.dwAllocationGranularity - 1);
292     if (alignment == 0)
293     {
294         return ::ClrVirtualAlloc(0, aligned_size, memFlags, PAGE_READWRITE);
295     }
296     else
297     {
298         return ::ClrVirtualAllocAligned(0, aligned_size, memFlags, PAGE_READWRITE, alignment);
299     }
300 }
301
302 // Release virtual memory range previously reserved using VirtualReserve
303 // Parameters:
304 //  address - starting virtual address
305 //  size    - size of the virtual memory range
306 // Return:
307 //  true if it has succeeded, false if it has failed
308 bool GCToOSInterface::VirtualRelease(void* address, size_t size)
309 {
310     LIMITED_METHOD_CONTRACT;
311
312     UNREFERENCED_PARAMETER(size);
313     return !!::ClrVirtualFree(address, 0, MEM_RELEASE);
314 }
315
316 // Commit virtual memory range.
317 // Parameters:
318 //  size      - size of the virtual memory range
319 // Return:
320 //  Starting virtual address of the committed range
321 void* GCToOSInterface::VirtualReserveAndCommitLargePages(size_t size)
322 {
323     LIMITED_METHOD_CONTRACT;
324
325 #if !defined(FEATURE_PAL)
326     if (!g_SeLockMemoryPrivilegeAcquired)
327     {
328         if (!InitLargePagesPrivilege())
329         {
330             return nullptr;
331         }
332
333         g_SeLockMemoryPrivilegeAcquired = true;
334     }
335
336     SIZE_T largePageMinimum = GetLargePageMinimum();
337     size = (size + (largePageMinimum - 1)) & ~(largePageMinimum - 1);
338 #endif
339
340     return ::ClrVirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE);
341 }
342
343 // Commit virtual memory range. It must be part of a range reserved using VirtualReserve.
344 // Parameters:
345 //  address - starting virtual address
346 //  size    - size of the virtual memory range
347 // Return:
348 //  true if it has succeeded, false if it has failed
349 bool GCToOSInterface::VirtualCommit(void* address, size_t size, uint16_t node)
350 {
351     LIMITED_METHOD_CONTRACT;
352
353     if (node == NUMA_NODE_UNDEFINED)
354     {
355         return ::ClrVirtualAlloc(address, size, MEM_COMMIT, PAGE_READWRITE) != NULL;
356     }
357     else
358     {
359         return NumaNodeInfo::VirtualAllocExNuma(::GetCurrentProcess(), address, size, MEM_COMMIT, PAGE_READWRITE, node) != NULL;
360     }
361 }
362
363 // Decomit virtual memory range.
364 // Parameters:
365 //  address - starting virtual address
366 //  size    - size of the virtual memory range
367 // Return:
368 //  true if it has succeeded, false if it has failed
369 bool GCToOSInterface::VirtualDecommit(void* address, size_t size)
370 {
371     LIMITED_METHOD_CONTRACT;
372
373     return !!::ClrVirtualFree(address, size, MEM_DECOMMIT);
374 }
375
376 // Reset virtual memory range. Indicates that data in the memory range specified by address and size is no 
377 // longer of interest, but it should not be decommitted.
378 // Parameters:
379 //  address - starting virtual address
380 //  size    - size of the virtual memory range
381 //  unlock  - true if the memory range should also be unlocked
382 // Return:
383 //  true if it has succeeded, false if it has failed
384 bool GCToOSInterface::VirtualReset(void * address, size_t size, bool unlock)
385 {
386     LIMITED_METHOD_CONTRACT;
387
388     bool success = ::ClrVirtualAlloc(address, size, MEM_RESET, PAGE_READWRITE) != NULL;
389 #ifndef FEATURE_PAL
390     if (success && unlock)
391     {
392         // Remove the page range from the working set
393         ::VirtualUnlock(address, size);
394     }
395 #endif // FEATURE_PAL
396
397     return success;
398 }
399
400 // Check if the OS supports write watching
401 bool GCToOSInterface::SupportsWriteWatch()
402 {
403     LIMITED_METHOD_CONTRACT;
404
405     bool writeWatchSupported = false;
406
407     // check if the OS supports write-watch. 
408     // Drawbridge does not support write-watch so we still need to do the runtime detection for them.
409     // Otherwise, all currently supported OSes do support write-watch.
410     void* mem = VirtualReserve (g_SystemInfo.dwAllocationGranularity, 0, VirtualReserveFlags::WriteWatch);
411     if (mem != NULL)
412     {
413         VirtualRelease (mem, g_SystemInfo.dwAllocationGranularity);
414         writeWatchSupported = true;
415     }
416
417     return writeWatchSupported;
418 }
419
420 // Reset the write tracking state for the specified virtual memory range.
421 // Parameters:
422 //  address - starting virtual address
423 //  size    - size of the virtual memory range
424 void GCToOSInterface::ResetWriteWatch(void* address, size_t size)
425 {
426     LIMITED_METHOD_CONTRACT;
427
428     ::ResetWriteWatch(address, size);
429 }
430
431 // Retrieve addresses of the pages that are written to in a region of virtual memory
432 // Parameters:
433 //  resetState         - true indicates to reset the write tracking state
434 //  address            - starting virtual address
435 //  size               - size of the virtual memory range
436 //  pageAddresses      - buffer that receives an array of page addresses in the memory region
437 //  pageAddressesCount - on input, size of the lpAddresses array, in array elements
438 //                       on output, the number of page addresses that are returned in the array.
439 // Return:
440 //  true if it has succeeded, false if it has failed
441 bool GCToOSInterface::GetWriteWatch(bool resetState, void* address, size_t size, void** pageAddresses, uintptr_t* pageAddressesCount)
442 {
443     LIMITED_METHOD_CONTRACT;
444
445     uint32_t flags = resetState ? 1 : 0;
446     ULONG granularity;
447
448     bool success = ::GetWriteWatch(flags, address, size, pageAddresses, (ULONG_PTR*)pageAddressesCount, &granularity) == 0;
449     _ASSERTE (granularity == GetOsPageSize());
450
451     return success;
452 }
453
454 // Get size of the largest cache on the processor die
455 // Parameters:
456 //  trueSize - true to return true cache size, false to return scaled up size based on
457 //             the processor architecture
458 // Return:
459 //  Size of the cache
460 size_t GCToOSInterface::GetCacheSizePerLogicalCpu(bool trueSize)
461 {
462     LIMITED_METHOD_CONTRACT;
463
464     return ::GetCacheSizePerLogicalCpu(trueSize);
465 }
466
467 // Sets the calling thread's affinity to only run on the processor specified
468 // Parameters:
469 //  procNo - The requested processor for the calling thread.
470 // Return:
471 //  true if setting the affinity was successful, false otherwise.
472 bool GCToOSInterface::SetThreadAffinity(uint16_t procNo)
473 {
474     LIMITED_METHOD_CONTRACT;
475
476     GroupProcNo groupProcNo(procNo);
477
478     if (groupProcNo.GetGroup() != GroupProcNo::NoGroup)
479     {
480         GROUP_AFFINITY ga;
481         ga.Group = (WORD)groupProcNo.GetGroup();
482         ga.Reserved[0] = 0; // reserve must be filled with zero
483         ga.Reserved[1] = 0; // otherwise call may fail
484         ga.Reserved[2] = 0;
485         ga.Mask = (size_t)1 << groupProcNo.GetProcIndex();
486         return !!SetThreadGroupAffinity(GetCurrentThread(), &ga, nullptr);
487     }
488     else
489     {
490         return !!SetThreadAffinityMask(GetCurrentThread(), (DWORD_PTR)1 << groupProcNo.GetProcIndex());
491     }
492 }
493
494 // Boosts the calling thread's thread priority to a level higher than the default
495 // for new threads.
496 // Parameters:
497 //  None.
498 // Return:
499 //  true if the priority boost was successful, false otherwise.
500 bool GCToOSInterface::BoostThreadPriority()
501 {
502     return !!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
503 }
504
505 // Set the set of processors enabled for GC threads for the current process based on config specified affinity mask and set
506 // Parameters:
507 //  configAffinityMask - mask specified by the GCHeapAffinitizeMask config
508 //  configAffinitySet  - affinity set specified by the GCHeapAffinitizeRanges config
509 // Return:
510 //  set of enabled processors
511 const AffinitySet* GCToOSInterface::SetGCThreadsAffinitySet(uintptr_t configAffinityMask, const AffinitySet* configAffinitySet)
512 {
513     if (CPUGroupInfo::CanEnableGCCPUGroups())
514     {
515         if (!configAffinitySet->IsEmpty())
516         {
517             // Update the process affinity set using the configured set
518             for (size_t i = 0; i < MAX_SUPPORTED_CPUS; i++)
519             {
520                 if (g_processAffinitySet.Contains(i) && !configAffinitySet->Contains(i))
521                 {
522                     g_processAffinitySet.Remove(i);
523                 }
524             }
525         }
526     }
527     else
528     {
529         if (configAffinityMask != 0)
530         {
531             // Update the process affinity set using the configured mask
532             for (size_t i = 0; i < 8 * sizeof(uintptr_t); i++)
533             {
534                 if (g_processAffinitySet.Contains(i) && ((configAffinityMask & ((uintptr_t)1 << i)) == 0))
535                 {
536                     g_processAffinitySet.Remove(i);
537                 }
538             }
539         }
540     }
541
542     return &g_processAffinitySet;
543 }
544
545 // Get number of processors assigned to the current process
546 // Return:
547 //  The number of processors
548 uint32_t GCToOSInterface::GetCurrentProcessCpuCount()
549 {
550     LIMITED_METHOD_CONTRACT;
551
552     // GetCurrentProcessCpuCount only returns up to 64 procs.
553     return CPUGroupInfo::CanEnableGCCPUGroups() ?
554                 GCToOSInterface::GetTotalProcessorCount():
555                 ::GetCurrentProcessCpuCount();
556 }
557
558 // Return the size of the user-mode portion of the virtual address space of this process.
559 // Return:
560 //  non zero if it has succeeded, 0 if it has failed
561 size_t GCToOSInterface::GetVirtualMemoryLimit()
562 {
563     LIMITED_METHOD_CONTRACT;
564
565     MEMORYSTATUSEX memStatus;
566     ::GetProcessMemoryLoad(&memStatus);
567
568     return (size_t)memStatus.ullTotalVirtual;
569 }
570
571 static size_t g_RestrictedPhysicalMemoryLimit = (size_t)MAX_PTR;
572
573 #ifndef FEATURE_PAL
574
575 // For 32-bit processes the virtual address range could be smaller than the amount of physical
576 // memory on the machine/in the container, we need to restrict by the VM.
577 static bool g_UseRestrictedVirtualMemory = false;
578
579 typedef BOOL (WINAPI *PGET_PROCESS_MEMORY_INFO)(HANDLE handle, PROCESS_MEMORY_COUNTERS* memCounters, uint32_t cb);
580 static PGET_PROCESS_MEMORY_INFO GCGetProcessMemoryInfo = 0;
581
582 typedef BOOL (WINAPI *PIS_PROCESS_IN_JOB)(HANDLE processHandle, HANDLE jobHandle, BOOL* result);
583 typedef BOOL (WINAPI *PQUERY_INFORMATION_JOB_OBJECT)(HANDLE jobHandle, JOBOBJECTINFOCLASS jobObjectInfoClass, void* lpJobObjectInfo, DWORD cbJobObjectInfoLength, LPDWORD lpReturnLength);
584
585 static size_t GetRestrictedPhysicalMemoryLimit()
586 {
587     LIMITED_METHOD_CONTRACT;
588
589     // The limit was cached already
590     if (g_RestrictedPhysicalMemoryLimit != (size_t)MAX_PTR)
591         return g_RestrictedPhysicalMemoryLimit;
592
593     size_t job_physical_memory_limit = (size_t)MAX_PTR;
594     uint64_t total_virtual = 0;
595     uint64_t total_physical = 0;
596     BOOL in_job_p = FALSE;
597     HINSTANCE hinstKernel32 = 0;
598
599     PIS_PROCESS_IN_JOB GCIsProcessInJob = 0;
600     PQUERY_INFORMATION_JOB_OBJECT GCQueryInformationJobObject = 0;
601
602     GCIsProcessInJob = &(::IsProcessInJob);
603
604     if (!GCIsProcessInJob(GetCurrentProcess(), NULL, &in_job_p))
605         goto exit;
606
607     if (in_job_p)
608     {
609         hinstKernel32 = WszLoadLibrary(L"kernel32.dll");
610         if (!hinstKernel32)
611             goto exit;
612
613         GCGetProcessMemoryInfo = (PGET_PROCESS_MEMORY_INFO)GetProcAddress(hinstKernel32, "K32GetProcessMemoryInfo");
614
615         if (!GCGetProcessMemoryInfo)
616             goto exit;
617
618         GCQueryInformationJobObject = &(::QueryInformationJobObject);
619
620         if (!GCQueryInformationJobObject)
621             goto exit;
622
623         JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info;
624         if (GCQueryInformationJobObject (NULL, JobObjectExtendedLimitInformation, &limit_info, 
625             sizeof(limit_info), NULL))
626         {
627             size_t job_memory_limit = (size_t)MAX_PTR;
628             size_t job_process_memory_limit = (size_t)MAX_PTR;
629             size_t job_workingset_limit = (size_t)MAX_PTR;
630
631             // Notes on the NT job object:
632             //
633             // You can specific a bigger process commit or working set limit than 
634             // job limit which is pointless so we use the smallest of all 3 as
635             // to calculate our "physical memory load" or "available physical memory"
636             // when running inside a job object, ie, we treat this as the amount of physical memory
637             // our process is allowed to use.
638             // 
639             // The commit limit is already reflected by default when you run in a 
640             // job but the physical memory load is not.
641             //
642             if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_JOB_MEMORY) != 0)
643                 job_memory_limit = limit_info.JobMemoryLimit;
644             if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_PROCESS_MEMORY) != 0)
645                 job_process_memory_limit = limit_info.ProcessMemoryLimit;
646             if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_WORKINGSET) != 0)
647                 job_workingset_limit = limit_info.BasicLimitInformation.MaximumWorkingSetSize;
648
649             if ((job_memory_limit != (size_t)MAX_PTR) ||
650                 (job_process_memory_limit != (size_t)MAX_PTR) ||
651                 (job_workingset_limit != (size_t)MAX_PTR))
652             {
653                 job_physical_memory_limit = min (job_memory_limit, job_process_memory_limit);
654                 job_physical_memory_limit = min (job_physical_memory_limit, job_workingset_limit);
655
656                 MEMORYSTATUSEX ms;
657                 ::GetProcessMemoryLoad(&ms);
658                 total_virtual = ms.ullTotalVirtual;
659                 total_physical = ms.ullAvailPhys;
660
661                 // A sanity check in case someone set a larger limit than there is actual physical memory.
662                 job_physical_memory_limit = (size_t) min (job_physical_memory_limit, ms.ullTotalPhys);
663             }
664         }
665     }
666
667 exit:
668     if (job_physical_memory_limit == (size_t)MAX_PTR)
669     {
670         job_physical_memory_limit = 0;
671
672         if (hinstKernel32 != 0)
673         {
674             FreeLibrary(hinstKernel32);
675             hinstKernel32 = 0;
676             GCGetProcessMemoryInfo = 0;
677         }
678     }
679
680     // Check to see if we are limited by VM.
681     if (total_virtual == 0)
682     {
683         MEMORYSTATUSEX ms;
684         ::GetProcessMemoryLoad(&ms);
685
686         total_virtual = ms.ullTotalVirtual;
687         total_physical = ms.ullTotalPhys;
688     }
689
690     if (job_physical_memory_limit != 0)
691     {
692         total_physical = job_physical_memory_limit;
693     }
694
695     if (total_virtual < total_physical)
696     {
697         if (hinstKernel32 != 0)
698         {
699             // We can also free the lib here - if we are limited by VM we will not be calling
700             // GetProcessMemoryInfo.
701             FreeLibrary(hinstKernel32);
702             GCGetProcessMemoryInfo = 0;
703         }
704         g_UseRestrictedVirtualMemory = true;
705         job_physical_memory_limit = (size_t)total_virtual;
706     }
707
708     VolatileStore(&g_RestrictedPhysicalMemoryLimit, job_physical_memory_limit);
709     return g_RestrictedPhysicalMemoryLimit;
710 }
711
712 #else
713
714 static size_t GetRestrictedPhysicalMemoryLimit()
715 {
716     LIMITED_METHOD_CONTRACT;
717
718     // The limit was cached already
719     if (g_RestrictedPhysicalMemoryLimit != (size_t)MAX_PTR)
720         return g_RestrictedPhysicalMemoryLimit;
721
722     size_t memory_limit = PAL_GetRestrictedPhysicalMemoryLimit();
723     
724     VolatileStore(&g_RestrictedPhysicalMemoryLimit, memory_limit);
725     return g_RestrictedPhysicalMemoryLimit;
726 }
727 #endif // FEATURE_PAL
728
729
730 // Get the physical memory that this process can use.
731 // Return:
732 //  non zero if it has succeeded, 0 if it has failed
733 //
734 // PERF TODO: Requires more work to not treat the restricted case to be special. 
735 // To be removed before 3.0 ships.
736 uint64_t GCToOSInterface::GetPhysicalMemoryLimit(bool* is_restricted)
737 {
738     LIMITED_METHOD_CONTRACT;
739
740     if (is_restricted)
741         *is_restricted = false;
742
743     size_t restricted_limit = GetRestrictedPhysicalMemoryLimit();
744     if (restricted_limit != 0)
745     {
746         if (is_restricted 
747 #ifndef FEATURE_PAL
748             && !g_UseRestrictedVirtualMemory
749 #endif
750             )
751             *is_restricted = true;
752
753         return restricted_limit;
754     }
755
756     MEMORYSTATUSEX memStatus;
757     ::GetProcessMemoryLoad(&memStatus);
758
759     return memStatus.ullTotalPhys;
760 }
761
762 // Get memory status
763 // Parameters:
764 //  memory_load - A number between 0 and 100 that specifies the approximate percentage of physical memory
765 //      that is in use (0 indicates no memory use and 100 indicates full memory use).
766 //  available_physical - The amount of physical memory currently available, in bytes.
767 //  available_page_file - The maximum amount of memory the current process can commit, in bytes.
768 // Remarks:
769 //  Any parameter can be null.
770 void GCToOSInterface::GetMemoryStatus(uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file)
771 {
772     LIMITED_METHOD_CONTRACT;
773
774     uint64_t restricted_limit = GetRestrictedPhysicalMemoryLimit();
775     if (restricted_limit != 0)
776     {
777         size_t workingSetSize;
778         BOOL status = FALSE;
779 #ifndef FEATURE_PAL
780         if (!g_UseRestrictedVirtualMemory)
781         {
782             PROCESS_MEMORY_COUNTERS pmc;
783             status = GCGetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc));
784             workingSetSize = pmc.WorkingSetSize;
785         }
786 #else
787         status = PAL_GetPhysicalMemoryUsed(&workingSetSize);
788 #endif
789         if(status)
790         {
791             if (memory_load)
792                 *memory_load = (uint32_t)((float)workingSetSize * 100.0 / (float)restricted_limit);
793             if (available_physical)
794             {
795                 if(workingSetSize > restricted_limit)
796                     *available_physical = 0;
797                 else
798                     *available_physical = restricted_limit - workingSetSize;
799             }
800             // Available page file doesn't mean much when physical memory is restricted since
801             // we don't know how much of it is available to this process so we are not going to 
802             // bother to make another OS call for it.
803             if (available_page_file)
804                 *available_page_file = 0;
805
806             return;
807         }
808     }
809
810     MEMORYSTATUSEX ms;
811     ::GetProcessMemoryLoad(&ms);
812     
813 #ifndef FEATURE_PAL
814     if (g_UseRestrictedVirtualMemory)
815     {
816         _ASSERTE (ms.ullTotalVirtual == restricted_limit);
817         if (memory_load != NULL)
818             *memory_load = (uint32_t)((float)(ms.ullTotalVirtual - ms.ullAvailVirtual) * 100.0 / (float)ms.ullTotalVirtual);
819         if (available_physical != NULL)
820             *available_physical = ms.ullTotalVirtual;
821
822         // Available page file isn't helpful when we are restricted by virtual memory
823         // since the amount of memory we can reserve is less than the amount of
824         // memory we can commit.
825         if (available_page_file != NULL)
826             *available_page_file = 0;
827     }
828     else
829 #endif //!FEATURE_PAL
830     {
831         if (memory_load != NULL)
832             *memory_load = ms.dwMemoryLoad;
833         if (available_physical != NULL)
834             *available_physical = ms.ullAvailPhys;
835         if (available_page_file != NULL)
836             *available_page_file = ms.ullAvailPageFile;
837     }
838 }
839
840 // Get a high precision performance counter
841 // Return:
842 //  The counter value
843 int64_t GCToOSInterface::QueryPerformanceCounter()
844 {
845     LIMITED_METHOD_CONTRACT;
846
847     LARGE_INTEGER ts;
848     if (!::QueryPerformanceCounter(&ts))
849     {
850         DebugBreak();
851         _ASSERTE(!"Fatal Error - cannot query performance counter.");
852         EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);        // TODO: fatal error        
853     }
854
855     return ts.QuadPart;
856 }
857
858 // Get a frequency of the high precision performance counter
859 // Return:
860 //  The counter frequency
861 int64_t GCToOSInterface::QueryPerformanceFrequency()
862 {
863     LIMITED_METHOD_CONTRACT;
864
865     LARGE_INTEGER frequency;
866     if (!::QueryPerformanceFrequency(&frequency))
867     {
868         DebugBreak();
869         _ASSERTE(!"Fatal Error - cannot query performance counter.");
870         EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);        // TODO: fatal error        
871     }
872
873     return frequency.QuadPart;
874 }
875
876 // Get a time stamp with a low precision
877 // Return:
878 //  Time stamp in milliseconds
879 uint32_t GCToOSInterface::GetLowPrecisionTimeStamp()
880 {
881     LIMITED_METHOD_CONTRACT;
882
883     return ::GetTickCount();
884 }
885
886 uint32_t GCToOSInterface::GetTotalProcessorCount()
887 {
888     LIMITED_METHOD_CONTRACT;
889
890     if (CPUGroupInfo::CanEnableGCCPUGroups())
891     {
892         return CPUGroupInfo::GetNumActiveProcessors();
893     }
894     else
895     {
896         return g_SystemInfo.dwNumberOfProcessors;
897     }
898 }
899
900 bool GCToOSInterface::CanEnableGCNumaAware()
901 {
902     LIMITED_METHOD_CONTRACT;
903
904     return NumaNodeInfo::CanEnableGCNumaAware() != FALSE;
905 }
906
907 bool GCToOSInterface::GetNumaProcessorNode(uint16_t proc_no, uint16_t *node_no)
908 {
909     LIMITED_METHOD_CONTRACT;
910
911     GroupProcNo groupProcNo(proc_no);
912
913     PROCESSOR_NUMBER procNumber;
914     procNumber.Group    = groupProcNo.GetGroup();
915     procNumber.Number   = (BYTE)groupProcNo.GetProcIndex();
916     procNumber.Reserved = 0;
917
918     return NumaNodeInfo::GetNumaProcessorNodeEx(&procNumber, node_no) != FALSE;
919 }
920
921 // Get processor number and optionally its NUMA node number for the specified heap number
922 // Parameters:
923 //  heap_number - heap number to get the result for
924 //  proc_no     - set to the selected processor number
925 //  node_no     - set to the NUMA node of the selected processor or to NUMA_NODE_UNDEFINED
926 // Return:
927 //  true if it succeeded
928 bool GCToOSInterface::GetProcessorForHeap(uint16_t heap_number, uint16_t* proc_no, uint16_t* node_no)
929 {
930     bool success = false;
931
932     if (CPUGroupInfo::CanEnableGCCPUGroups())
933     {
934         uint16_t gn, gpn;
935         CPUGroupInfo::GetGroupForProcessor((uint16_t)heap_number, &gn, &gpn);
936
937         *proc_no = GroupProcNo(gn, gpn).GetCombinedValue();
938         if (GCToOSInterface::CanEnableGCNumaAware())
939         {
940             if (!GCToOSInterface::GetNumaProcessorNode(*proc_no, node_no))
941             {
942                 *node_no = NUMA_NODE_UNDEFINED;
943             }
944         }
945         else
946         {   // no numa setting, each cpu group is treated as a node
947             *node_no = gn;
948         }
949
950         success = true;
951     }
952     else
953     {
954         int bit_number = 0;
955         uint8_t proc_number = 0;
956         for (uintptr_t mask = 1; mask != 0; mask <<= 1)
957         {
958             if (g_processAffinitySet.Contains(proc_number))
959             {
960                 if (bit_number == heap_number)
961                 {
962                     *proc_no = GroupProcNo(GroupProcNo::NoGroup, proc_number).GetCombinedValue();
963
964                     if (GCToOSInterface::CanEnableGCNumaAware())
965                     {
966                         if (!GCToOSInterface::GetNumaProcessorNode(proc_number, node_no))
967                         {
968                             *node_no = NUMA_NODE_UNDEFINED;
969                         }
970                     }
971
972                     success = true;
973                     break;
974                 }
975                 bit_number++;
976             }
977             proc_number++;
978         }
979     }
980
981     return success;
982 }
983
984 // Parse the confing string describing affinitization ranges and update the passed in affinitySet accordingly
985 // Parameters:
986 //  config_string - string describing the affinitization range, platform specific
987 //  start_index  - the range start index extracted from the config_string
988 //  end_index    - the range end index extracted from the config_string, equal to the start_index if only an index and not a range was passed in
989 // Return:
990 //  true if the configString was successfully parsed, false if it was not correct
991 bool GCToOSInterface::ParseGCHeapAffinitizeRangesEntry(const char** config_string, size_t* start_index, size_t* end_index)
992 {
993     size_t index_offset = 0;
994
995     char* number_end;
996     size_t group_number = strtoul(*config_string, &number_end, 10);
997
998     if ((number_end == *config_string) || (*number_end != ':'))
999     {
1000         // No number or no colon after the number found, invalid format
1001         return false;
1002     }
1003
1004     WORD group_begin;
1005     WORD group_size;
1006     if (!CPUGroupInfo::GetCPUGroupRange((WORD)group_number, &group_begin, &group_size))
1007     {
1008         // group number out of range
1009         return false;
1010     }
1011
1012     index_offset = group_begin;
1013     *config_string = number_end + 1;
1014
1015     size_t start, end;
1016     if (!ParseIndexOrRange(config_string, &start, &end))
1017     {
1018         return false;
1019     }
1020
1021     if ((start >= group_size) || (end >= group_size))
1022     {
1023         // Invalid CPU index values or range
1024         return false;
1025     }
1026
1027     *start_index = index_offset + start;
1028     *end_index = index_offset + end;
1029
1030     return true;
1031 }
1032
1033 // Initialize the critical section
1034 void CLRCriticalSection::Initialize()
1035 {
1036     WRAPPER_NO_CONTRACT;
1037     UnsafeInitializeCriticalSection(&m_cs);
1038 }
1039
1040 // Destroy the critical section
1041 void CLRCriticalSection::Destroy()
1042 {
1043     WRAPPER_NO_CONTRACT;
1044     UnsafeDeleteCriticalSection(&m_cs);
1045 }
1046
1047 // Enter the critical section. Blocks until the section can be entered.
1048 void CLRCriticalSection::Enter()
1049 {
1050     WRAPPER_NO_CONTRACT;
1051     UnsafeEnterCriticalSection(&m_cs);
1052 }
1053
1054 // Leave the critical section
1055 void CLRCriticalSection::Leave()
1056 {
1057     WRAPPER_NO_CONTRACT;
1058     UnsafeLeaveCriticalSection(&m_cs);
1059 }
1060
1061 // An implementatino of GCEvent that delegates to
1062 // a CLREvent, which in turn delegates to the PAL. This event
1063 // is also host-aware.
1064 class GCEvent::Impl
1065 {
1066 private:
1067     CLREvent m_event;
1068
1069 public:
1070     Impl() = default;
1071
1072     bool IsValid()
1073     {
1074         WRAPPER_NO_CONTRACT;
1075
1076         return !!m_event.IsValid();
1077     }
1078
1079     void CloseEvent()
1080     {
1081         WRAPPER_NO_CONTRACT;
1082
1083         assert(m_event.IsValid());
1084         m_event.CloseEvent();
1085     }
1086
1087     void Set()
1088     {
1089         WRAPPER_NO_CONTRACT;
1090
1091         assert(m_event.IsValid());
1092         m_event.Set();
1093     }
1094
1095     void Reset()
1096     {
1097         WRAPPER_NO_CONTRACT;
1098
1099         assert(m_event.IsValid());
1100         m_event.Reset();
1101     }
1102
1103     uint32_t Wait(uint32_t timeout, bool alertable)
1104     {
1105         WRAPPER_NO_CONTRACT;
1106
1107         assert(m_event.IsValid());
1108         return m_event.Wait(timeout, alertable);
1109     }
1110
1111     bool CreateAutoEvent(bool initialState)
1112     {
1113         CONTRACTL {
1114             NOTHROW;
1115             GC_NOTRIGGER;
1116         } CONTRACTL_END;
1117
1118         return !!m_event.CreateAutoEventNoThrow(initialState);
1119     }
1120
1121     bool CreateManualEvent(bool initialState)
1122     {
1123         CONTRACTL {
1124             NOTHROW;
1125             GC_NOTRIGGER;
1126         } CONTRACTL_END;
1127
1128         return !!m_event.CreateManualEventNoThrow(initialState);
1129     }
1130
1131     bool CreateOSAutoEvent(bool initialState)
1132     {
1133         CONTRACTL {
1134             NOTHROW;
1135             GC_NOTRIGGER;
1136         } CONTRACTL_END;
1137
1138         return !!m_event.CreateOSAutoEventNoThrow(initialState);
1139     }
1140
1141     bool CreateOSManualEvent(bool initialState)
1142     {
1143         CONTRACTL {
1144             NOTHROW;
1145             GC_NOTRIGGER;
1146         } CONTRACTL_END;
1147
1148         return !!m_event.CreateOSManualEventNoThrow(initialState);
1149     }
1150 };
1151
1152 GCEvent::GCEvent()
1153   : m_impl(nullptr)
1154 {
1155 }
1156
1157 void GCEvent::CloseEvent()
1158 {
1159     WRAPPER_NO_CONTRACT;
1160
1161     assert(m_impl != nullptr);
1162     m_impl->CloseEvent();
1163 }
1164
1165 void GCEvent::Set()
1166 {
1167     WRAPPER_NO_CONTRACT;
1168
1169     assert(m_impl != nullptr);
1170     m_impl->Set();
1171 }
1172
1173 void GCEvent::Reset()
1174 {
1175     WRAPPER_NO_CONTRACT;
1176
1177     assert(m_impl != nullptr);
1178     m_impl->Reset();
1179 }
1180
1181 uint32_t GCEvent::Wait(uint32_t timeout, bool alertable)
1182 {
1183     WRAPPER_NO_CONTRACT;
1184
1185     assert(m_impl != nullptr);
1186     return m_impl->Wait(timeout, alertable);
1187 }
1188
1189 bool GCEvent::CreateManualEventNoThrow(bool initialState)
1190 {
1191     CONTRACTL {
1192       NOTHROW;
1193       GC_NOTRIGGER;
1194     } CONTRACTL_END;
1195
1196     assert(m_impl == nullptr);
1197     NewHolder<GCEvent::Impl> event = new (nothrow) GCEvent::Impl();
1198     if (!event)
1199     {
1200         return false;
1201     }
1202
1203     event->CreateManualEvent(initialState);
1204     m_impl = event.Extract();
1205     return true;
1206 }
1207
1208 bool GCEvent::CreateAutoEventNoThrow(bool initialState)
1209 {
1210     CONTRACTL {
1211       NOTHROW;
1212       GC_NOTRIGGER;
1213     } CONTRACTL_END;
1214
1215     assert(m_impl == nullptr);
1216     NewHolder<GCEvent::Impl> event = new (nothrow) GCEvent::Impl();
1217     if (!event)
1218     {
1219         return false;
1220     }
1221
1222     event->CreateAutoEvent(initialState);
1223     m_impl = event.Extract();
1224     return IsValid();
1225 }
1226
1227 bool GCEvent::CreateOSAutoEventNoThrow(bool initialState)
1228 {
1229     CONTRACTL {
1230       NOTHROW;
1231       GC_NOTRIGGER;
1232     } CONTRACTL_END;
1233
1234     assert(m_impl == nullptr);
1235     NewHolder<GCEvent::Impl> event = new (nothrow) GCEvent::Impl();
1236     if (!event)
1237     {
1238         return false;
1239     }
1240
1241     event->CreateOSAutoEvent(initialState);
1242     m_impl = event.Extract();
1243     return IsValid();
1244 }
1245
1246 bool GCEvent::CreateOSManualEventNoThrow(bool initialState)
1247 {
1248     CONTRACTL {
1249       NOTHROW;
1250       GC_NOTRIGGER;
1251     } CONTRACTL_END;
1252
1253     assert(m_impl == nullptr);
1254     NewHolder<GCEvent::Impl> event = new (nothrow) GCEvent::Impl();
1255     if (!event)
1256     {
1257         return false;
1258     }
1259
1260     event->CreateOSManualEvent(initialState);
1261     m_impl = event.Extract();
1262     return IsValid();
1263 }
1264