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 // Interface between GC and the OS specific functionality
11 // This is a funny workaround for the fact that "common.h" defines Sleep to be
12 // Dont_Use_Sleep, with the hope of causing linker errors whenever someone tries to use sleep.
14 // However, GCToOSInterface defines a function called Sleep, which (due to this define) becomes
15 // "Dont_Use_Sleep", which the GC in turn happily uses. The symbol that GCToOSInterface actually
16 // exported was called "GCToOSInterface::Dont_Use_Sleep". While we progress in making the GC standalone,
17 // we'll need to break the dependency on common.h (the VM header) and this problem will become moot.
21 #ifdef HAS_SYSTEM_YIELDPROCESSOR
22 // YieldProcessor is defined to Dont_Use_YieldProcessor. Restore it to the system-default implementation for the GC.
24 #define YieldProcessor System_YieldProcessor
27 #define NUMA_NODE_UNDEFINED UINT16_MAX
29 bool ParseIndexOrRange(const char** config_string, size_t* start_index, size_t* end_index);
31 // Critical section used by the GC
32 class CLRCriticalSection
34 CRITICAL_SECTION m_cs;
37 // Initialize the critical section
40 // Destroy the critical section
43 // Enter the critical section. Blocks until the section can be entered.
46 // Leave the critical section
50 // Flags for the GCToOSInterface::VirtualReserve method
51 struct VirtualReserveFlags
60 // An event is a synchronization object whose state can be set and reset
61 // indicating that an event has occured. It is used pervasively throughout
64 // Note that GCEvent deliberately leaks its contents by not having a non-trivial destructor.
65 // This is by design; since all uses of GCEvent have static lifetime, their destructors
66 // are run on process exit, potentially concurrently with other threads that may still be
67 // operating on the static event. To avoid these sorts of unsafety, GCEvent chooses to
68 // not have a destructor at all. The cost of this is leaking a small amount of memory, but
69 // this is not a problem since a majority of the uses of GCEvent are static. See CoreCLR#11111
70 // for more details on the hazards of static destructors.
77 // Constructs a new uninitialized event.
80 // Closes the event. Attempting to use the event past calling CloseEvent
84 // "Sets" the event, indicating that a particular event has occured. May
85 // wake up other threads waiting on this event. Depending on whether or
86 // not this event is an auto-reset event, the state of the event may
87 // or may not be automatically reset after Set is called.
90 // Resets the event, resetting it back to a non-signalled state. Auto-reset
91 // events automatically reset once the event is set, while manual-reset
92 // events do not reset until Reset is called. It is a no-op to call Reset
93 // on an auto-reset event.
96 // Waits for some period of time for this event to be signalled. The
97 // period of time may be infinite (if the timeout argument is INFINITE) or
98 // it may be a specified period of time, in milliseconds.
100 // One of three values, depending on how why this thread was awoken:
101 // WAIT_OBJECT_0 - This event was signalled and woke up this thread.
102 // WAIT_TIMEOUT - The timeout interval expired without this event being signalled.
103 // WAIT_FAILED - The wait failed.
104 uint32_t Wait(uint32_t timeout, bool alertable);
106 // Determines whether or not this event is valid.
108 // true if this event is invalid (i.e. it has not yet been initialized or
109 // has already been closed), false otherwise
112 return m_impl != nullptr;
115 // Initializes this event to be a host-aware manual reset event with the
116 // given initial state.
118 // true if the initialization succeeded, false if it did not
119 bool CreateManualEventNoThrow(bool initialState);
121 // Initializes this event to be a host-aware auto-resetting event with the
122 // given initial state.
124 // true if the initialization succeeded, false if it did not
125 bool CreateAutoEventNoThrow(bool initialState);
127 // Initializes this event to be a host-unaware manual reset event with the
128 // given initial state.
130 // true if the initialization succeeded, false if it did not
131 bool CreateOSManualEventNoThrow(bool initialState);
133 // Initializes this event to be a host-unaware auto-resetting event with the
134 // given initial state.
136 // true if the initialization succeeded, false if it did not
137 bool CreateOSAutoEventNoThrow(bool initialState);
140 // GC thread function prototype
141 typedef void (*GCThreadFunction)(void* param);
144 // Right now we support maximum 1024 procs - meaning that we will create at most
145 // that many GC threads and GC heaps.
146 #define MAX_SUPPORTED_CPUS 1024
148 #define MAX_SUPPORTED_CPUS 64
151 // Add of processor indices used to store affinity.
154 static const size_t BitsPerBitsetEntry = 8 * sizeof(uintptr_t);
156 uintptr_t m_bitset[MAX_SUPPORTED_CPUS / BitsPerBitsetEntry];
158 static uintptr_t GetBitsetEntryMask(size_t cpuIndex)
160 return (uintptr_t)1 << (cpuIndex & (BitsPerBitsetEntry - 1));
163 static size_t GetBitsetEntryIndex(size_t cpuIndex)
165 return cpuIndex / BitsPerBitsetEntry;
172 memset(m_bitset, 0, sizeof(m_bitset));
175 // Check if the set contains a processor
176 bool Contains(size_t cpuIndex) const
178 return (m_bitset[GetBitsetEntryIndex(cpuIndex)] & GetBitsetEntryMask(cpuIndex)) != 0;
181 // Add a processor to the set
182 void Add(size_t cpuIndex)
184 m_bitset[GetBitsetEntryIndex(cpuIndex)] |= GetBitsetEntryMask(cpuIndex);
187 // Remove a processor from the set
188 void Remove(size_t cpuIndex)
190 m_bitset[GetBitsetEntryIndex(cpuIndex)] &= ~GetBitsetEntryMask(cpuIndex);
193 // Check if the set is empty
196 for (size_t i = 0; i < MAX_SUPPORTED_CPUS / BitsPerBitsetEntry; i++)
198 if (m_bitset[i] != 0)
207 // Return number of processors in the affinity set
211 for (size_t i = 0; i < MAX_SUPPORTED_CPUS; i++)
223 // Interface that the GC uses to invoke OS specific functionality
224 class GCToOSInterface
229 // Initialization and shutdown of the interface
232 // Initialize the interface implementation
234 // true if it has succeeded, false if it has failed
235 static bool Initialize();
237 // Shutdown the interface implementation
238 static void Shutdown();
241 // Virtual memory management
244 // Reserve virtual memory range.
246 // size - size of the virtual memory range
247 // alignment - requested memory alignment
248 // flags - flags to control special settings like write watching
250 // Starting virtual address of the reserved range
252 // Previous uses of this API aligned the `size` parameter to the platform
253 // allocation granularity. This is not required by POSIX or Windows. Windows will
254 // round the size up to the nearest page boundary. POSIX does not specify what is done,
255 // but Linux probably also rounds up. If an implementation of GCToOSInterface needs to
256 // align to the allocation granularity, it will do so in its implementation.
258 // Windows guarantees that the returned mapping will be aligned to the allocation
260 static void* VirtualReserve(size_t size, size_t alignment, uint32_t flags);
262 // Release virtual memory range previously reserved using VirtualReserve
264 // address - starting virtual address
265 // size - size of the virtual memory range
267 // true if it has succeeded, false if it has failed
268 static bool VirtualRelease(void *address, size_t size);
270 // Commit virtual memory range. It must be part of a range reserved using VirtualReserve.
272 // address - starting virtual address
273 // size - size of the virtual memory range
275 // true if it has succeeded, false if it has failed
276 static bool VirtualCommit(void *address, size_t size, uint16_t node = NUMA_NODE_UNDEFINED);
278 // Decomit virtual memory range.
280 // address - starting virtual address
281 // size - size of the virtual memory range
283 // true if it has succeeded, false if it has failed
284 static bool VirtualDecommit(void *address, size_t size);
286 // Reset virtual memory range. Indicates that data in the memory range specified by address and size is no
287 // longer of interest, but it should not be decommitted.
289 // address - starting virtual address
290 // size - size of the virtual memory range
291 // unlock - true if the memory range should also be unlocked
293 // true if it has succeeded, false if it has failed
294 static bool VirtualReset(void *address, size_t size, bool unlock);
300 // Check if the OS supports write watching
301 static bool SupportsWriteWatch();
303 // Reset the write tracking state for the specified virtual memory range.
305 // address - starting virtual address
306 // size - size of the virtual memory range
307 static void ResetWriteWatch(void *address, size_t size);
309 // Retrieve addresses of the pages that are written to in a region of virtual memory
311 // resetState - true indicates to reset the write tracking state
312 // address - starting virtual address
313 // size - size of the virtual memory range
314 // pageAddresses - buffer that receives an array of page addresses in the memory region
315 // pageAddressesCount - on input, size of the lpAddresses array, in array elements
316 // on output, the number of page addresses that are returned in the array.
318 // true if it has succeeded, false if it has failed
319 static bool GetWriteWatch(bool resetState, void* address, size_t size, void** pageAddresses, uintptr_t* pageAddressesCount);
322 // Thread and process
325 // Causes the calling thread to sleep for the specified number of milliseconds
327 // sleepMSec - time to sleep before switching to another thread
328 static void Sleep(uint32_t sleepMSec);
330 // Causes the calling thread to yield execution to another thread that is ready to run on the current processor.
332 // switchCount - number of times the YieldThread was called in a loop
333 static void YieldThread(uint32_t switchCount);
335 // Get the number of the current processor
336 static uint32_t GetCurrentProcessorNumber();
338 // Check if the OS supports getting current processor number
339 static bool CanGetCurrentProcessorNumber();
341 // Set ideal processor for the current thread
343 // srcProcNo - processor number the thread currently runs on
344 // dstProcNo - processor number the thread should be migrated to
346 // true if it has succeeded, false if it has failed
347 static bool SetCurrentThreadIdealAffinity(uint16_t srcProcNo, uint16_t dstProcNo);
349 // Get numeric id of the current thread if possible on the
350 // current platform. It is indended for logging purposes only.
352 // Numeric id of the current thread or 0 if the
353 static uint64_t GetCurrentThreadIdForLogging();
355 // Get id of the current process
357 // Id of the current process
358 static uint32_t GetCurrentProcessId();
361 // Processor topology
364 // Get size of the on die cache per logical processor
366 // trueSize - true to return true cache size, false to return scaled up size based on
367 // the processor architecture
370 static size_t GetCacheSizePerLogicalCpu(bool trueSize = true);
372 // Get number of processors assigned to the current process
374 // The number of processors
375 static uint32_t GetCurrentProcessCpuCount();
377 // Sets the calling thread's affinity to only run on the processor specified.
379 // procNo - The requested affinity for the calling thread.
382 // true if setting the affinity was successful, false otherwise.
383 static bool SetThreadAffinity(uint16_t procNo);
385 // Boosts the calling thread's thread priority to a level higher than the default
390 // true if the priority boost was successful, false otherwise.
391 static bool BoostThreadPriority();
393 // Set the set of processors enabled for GC threads for the current process based on config specified affinity mask and set
395 // configAffinityMask - mask specified by the GCHeapAffinitizeMask config
396 // configAffinitySet - affinity set specified by the GCHeapAffinitizeRanges config
398 // set of enabled processors
399 static const AffinitySet* SetGCThreadsAffinitySet(uintptr_t configAffinityMask, const AffinitySet* configAffinitySet);
402 // Global memory info
405 // Return the size of the user-mode portion of the virtual address space of this process.
407 // non zero if it has succeeded, 0 if it has failed
408 static size_t GetVirtualMemoryLimit();
410 // Get the physical memory that this process can use.
412 // non zero if it has succeeded, 0 if it has failed
413 // *is_restricted is set to true if asked and running in restricted.
415 // If a process runs with a restricted memory limit, it returns the limit. If there's no limit
416 // specified, it returns amount of actual physical memory.
418 // PERF TODO: Requires more work to not treat the restricted case to be special.
419 // To be removed before 3.0 ships.
420 static uint64_t GetPhysicalMemoryLimit(bool* is_restricted=NULL);
424 // memory_load - A number between 0 and 100 that specifies the approximate percentage of physical memory
425 // that is in use (0 indicates no memory use and 100 indicates full memory use).
426 // available_physical - The amount of physical memory currently available, in bytes.
427 // available_page_file - The maximum amount of memory the current process can commit, in bytes.
429 // Any parameter can be null.
430 static void GetMemoryStatus(uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file);
432 // Get size of an OS memory page
433 static uint32_t GetPageSize();
439 // Flush write buffers of processors that are executing threads of the current process
440 static void FlushProcessWriteBuffers();
442 // Break into a debugger
443 static void DebugBreak();
449 // Get a high precision performance counter
452 static int64_t QueryPerformanceCounter();
454 // Get a frequency of the high precision performance counter
456 // The counter frequency
457 static int64_t QueryPerformanceFrequency();
459 // Get a time stamp with a low precision
461 // Time stamp in milliseconds
462 static uint32_t GetLowPrecisionTimeStamp();
464 // Gets the total number of processors on the machine, not taking
465 // into account current process affinity.
467 // Number of processors on the machine
468 static uint32_t GetTotalProcessorCount();
470 // Is NUMA support available
471 static bool CanEnableGCNumaAware();
473 // Gets the NUMA node for the processor
474 static bool GetNumaProcessorNode(uint16_t proc_no, uint16_t *node_no);
476 // Get processor number and optionally its NUMA node number for the specified heap number
478 // heap_number - heap number to get the result for
479 // proc_no - set to the selected processor number
480 // node_no - set to the NUMA node of the selected processor or to NUMA_NODE_UNDEFINED
482 // true if it succeeded
483 static bool GetProcessorForHeap(uint16_t heap_number, uint16_t* proc_no, uint16_t* node_no);
485 // Parse the confing string describing affinitization ranges and update the passed in affinitySet accordingly
487 // config_string - string describing the affinitization range, platform specific
488 // start_index - the range start index extracted from the config_string
489 // 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
491 // true if the configString was successfully parsed, false if it was not correct
492 static bool ParseGCHeapAffinitizeRangesEntry(const char** config_string, size_t* start_index, size_t* end_index);
496 #endif // __GCENV_OS_H__