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
5//
6
7#ifndef __GCENV_OS_H__
8#define __GCENV_OS_H__
9
10#ifdef Sleep
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.
13//
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.
18#undef Sleep
19#endif // Sleep
20
21#define NUMA_NODE_UNDEFINED UINT32_MAX
22
23// Critical section used by the GC
24class CLRCriticalSection
25{
26 CRITICAL_SECTION m_cs;
27
28public:
29 // Initialize the critical section
30 void Initialize();
31
32 // Destroy the critical section
33 void Destroy();
34
35 // Enter the critical section. Blocks until the section can be entered.
36 void Enter();
37
38 // Leave the critical section
39 void Leave();
40};
41
42// Flags for the GCToOSInterface::VirtualReserve method
43struct VirtualReserveFlags
44{
45 enum
46 {
47 None = 0,
48 WriteWatch = 1,
49 };
50};
51
52// Affinity of a GC thread
53struct GCThreadAffinity
54{
55 static const int None = -1;
56
57 // Processor group index, None if no group is specified
58 int Group;
59 // Processor index, None if no affinity is specified
60 int Processor;
61};
62
63// An event is a synchronization object whose state can be set and reset
64// indicating that an event has occured. It is used pervasively throughout
65// the GC.
66//
67// Note that GCEvent deliberately leaks its contents by not having a non-trivial destructor.
68// This is by design; since all uses of GCEvent have static lifetime, their destructors
69// are run on process exit, potentially concurrently with other threads that may still be
70// operating on the static event. To avoid these sorts of unsafety, GCEvent chooses to
71// not have a destructor at all. The cost of this is leaking a small amount of memory, but
72// this is not a problem since a majority of the uses of GCEvent are static. See CoreCLR#11111
73// for more details on the hazards of static destructors.
74class GCEvent {
75private:
76 class Impl;
77 Impl *m_impl;
78
79public:
80 // Constructs a new uninitialized event.
81 GCEvent();
82
83 // Closes the event. Attempting to use the event past calling CloseEvent
84 // is a logic error.
85 void CloseEvent();
86
87 // "Sets" the event, indicating that a particular event has occured. May
88 // wake up other threads waiting on this event. Depending on whether or
89 // not this event is an auto-reset event, the state of the event may
90 // or may not be automatically reset after Set is called.
91 void Set();
92
93 // Resets the event, resetting it back to a non-signalled state. Auto-reset
94 // events automatically reset once the event is set, while manual-reset
95 // events do not reset until Reset is called. It is a no-op to call Reset
96 // on an auto-reset event.
97 void Reset();
98
99 // Waits for some period of time for this event to be signalled. The
100 // period of time may be infinite (if the timeout argument is INFINITE) or
101 // it may be a specified period of time, in milliseconds.
102 // Returns:
103 // One of three values, depending on how why this thread was awoken:
104 // WAIT_OBJECT_0 - This event was signalled and woke up this thread.
105 // WAIT_TIMEOUT - The timeout interval expired without this event being signalled.
106 // WAIT_FAILED - The wait failed.
107 uint32_t Wait(uint32_t timeout, bool alertable);
108
109 // Determines whether or not this event is valid.
110 // Returns:
111 // true if this event is invalid (i.e. it has not yet been initialized or
112 // has already been closed), false otherwise
113 bool IsValid() const
114 {
115 return m_impl != nullptr;
116 }
117
118 // Initializes this event to be a host-aware manual reset event with the
119 // given initial state.
120 // Returns:
121 // true if the initialization succeeded, false if it did not
122 bool CreateManualEventNoThrow(bool initialState);
123
124 // Initializes this event to be a host-aware auto-resetting event with the
125 // given initial state.
126 // Returns:
127 // true if the initialization succeeded, false if it did not
128 bool CreateAutoEventNoThrow(bool initialState);
129
130 // Initializes this event to be a host-unaware manual reset event with the
131 // given initial state.
132 // Returns:
133 // true if the initialization succeeded, false if it did not
134 bool CreateOSManualEventNoThrow(bool initialState);
135
136 // Initializes this event to be a host-unaware auto-resetting event with the
137 // given initial state.
138 // Returns:
139 // true if the initialization succeeded, false if it did not
140 bool CreateOSAutoEventNoThrow(bool initialState);
141};
142
143// GC thread function prototype
144typedef void (*GCThreadFunction)(void* param);
145
146// Interface that the GC uses to invoke OS specific functionality
147class GCToOSInterface
148{
149public:
150
151 //
152 // Initialization and shutdown of the interface
153 //
154
155 // Initialize the interface implementation
156 // Return:
157 // true if it has succeeded, false if it has failed
158 static bool Initialize();
159
160 // Shutdown the interface implementation
161 static void Shutdown();
162
163 //
164 // Virtual memory management
165 //
166
167 // Reserve virtual memory range.
168 // Parameters:
169 // size - size of the virtual memory range
170 // alignment - requested memory alignment
171 // flags - flags to control special settings like write watching
172 // Return:
173 // Starting virtual address of the reserved range
174 // Notes:
175 // Previous uses of this API aligned the `size` parameter to the platform
176 // allocation granularity. This is not required by POSIX or Windows. Windows will
177 // round the size up to the nearest page boundary. POSIX does not specify what is done,
178 // but Linux probably also rounds up. If an implementation of GCToOSInterface needs to
179 // align to the allocation granularity, it will do so in its implementation.
180 //
181 // Windows guarantees that the returned mapping will be aligned to the allocation
182 // granularity.
183 static void* VirtualReserve(size_t size, size_t alignment, uint32_t flags);
184
185 // Release virtual memory range previously reserved using VirtualReserve
186 // Parameters:
187 // address - starting virtual address
188 // size - size of the virtual memory range
189 // Return:
190 // true if it has succeeded, false if it has failed
191 static bool VirtualRelease(void *address, size_t size);
192
193 // Commit virtual memory range. It must be part of a range reserved using VirtualReserve.
194 // Parameters:
195 // address - starting virtual address
196 // size - size of the virtual memory range
197 // Return:
198 // true if it has succeeded, false if it has failed
199 static bool VirtualCommit(void *address, size_t size, uint32_t node = NUMA_NODE_UNDEFINED);
200
201 // Decomit virtual memory range.
202 // Parameters:
203 // address - starting virtual address
204 // size - size of the virtual memory range
205 // Return:
206 // true if it has succeeded, false if it has failed
207 static bool VirtualDecommit(void *address, size_t size);
208
209 // Reset virtual memory range. Indicates that data in the memory range specified by address and size is no
210 // longer of interest, but it should not be decommitted.
211 // Parameters:
212 // address - starting virtual address
213 // size - size of the virtual memory range
214 // unlock - true if the memory range should also be unlocked
215 // Return:
216 // true if it has succeeded, false if it has failed
217 static bool VirtualReset(void *address, size_t size, bool unlock);
218
219 //
220 // Write watching
221 //
222
223 // Check if the OS supports write watching
224 static bool SupportsWriteWatch();
225
226 // Reset the write tracking state for the specified virtual memory range.
227 // Parameters:
228 // address - starting virtual address
229 // size - size of the virtual memory range
230 static void ResetWriteWatch(void *address, size_t size);
231
232 // Retrieve addresses of the pages that are written to in a region of virtual memory
233 // Parameters:
234 // resetState - true indicates to reset the write tracking state
235 // address - starting virtual address
236 // size - size of the virtual memory range
237 // pageAddresses - buffer that receives an array of page addresses in the memory region
238 // pageAddressesCount - on input, size of the lpAddresses array, in array elements
239 // on output, the number of page addresses that are returned in the array.
240 // Return:
241 // true if it has succeeded, false if it has failed
242 static bool GetWriteWatch(bool resetState, void* address, size_t size, void** pageAddresses, uintptr_t* pageAddressesCount);
243
244 //
245 // Thread and process
246 //
247
248 // Causes the calling thread to sleep for the specified number of milliseconds
249 // Parameters:
250 // sleepMSec - time to sleep before switching to another thread
251 static void Sleep(uint32_t sleepMSec);
252
253 // Causes the calling thread to yield execution to another thread that is ready to run on the current processor.
254 // Parameters:
255 // switchCount - number of times the YieldThread was called in a loop
256 static void YieldThread(uint32_t switchCount);
257
258 // Get the number of the current processor
259 static uint32_t GetCurrentProcessorNumber();
260
261 // Check if the OS supports getting current processor number
262 static bool CanGetCurrentProcessorNumber();
263
264 // Set ideal processor for the current thread
265 // Parameters:
266 // processorIndex - index of the processor in the group
267 // affinity - ideal processor affinity for the thread
268 // Return:
269 // true if it has succeeded, false if it has failed
270 static bool SetCurrentThreadIdealAffinity(GCThreadAffinity* affinity);
271
272 // Get numeric id of the current thread if possible on the
273 // current platform. It is indended for logging purposes only.
274 // Return:
275 // Numeric id of the current thread or 0 if the
276 static uint64_t GetCurrentThreadIdForLogging();
277
278 // Get id of the current process
279 // Return:
280 // Id of the current process
281 static uint32_t GetCurrentProcessId();
282
283 //
284 // Processor topology
285 //
286
287 // Get size of the on die cache per logical processor
288 // Parameters:
289 // trueSize - true to return true cache size, false to return scaled up size based on
290 // the processor architecture
291 // Return:
292 // Size of the cache
293 static size_t GetCacheSizePerLogicalCpu(bool trueSize = true);
294
295 // Get number of processors assigned to the current process
296 // Return:
297 // The number of processors
298 static uint32_t GetCurrentProcessCpuCount();
299
300 // Sets the calling thread's affinity to only run on the processor specified
301 // in the GCThreadAffinity structure.
302 // Parameters:
303 // affinity - The requested affinity for the calling thread. At most one processor
304 // can be provided.
305 // Return:
306 // true if setting the affinity was successful, false otherwise.
307 static bool SetThreadAffinity(GCThreadAffinity* affinity);
308
309 // Boosts the calling thread's thread priority to a level higher than the default
310 // for new threads.
311 // Parameters:
312 // None.
313 // Return:
314 // true if the priority boost was successful, false otherwise.
315 static bool BoostThreadPriority();
316
317 // Get affinity mask of the current process
318 // Parameters:
319 // processMask - affinity mask for the specified process
320 // systemMask - affinity mask for the system
321 // Return:
322 // true if it has succeeded, false if it has failed
323 // Remarks:
324 // A process affinity mask is a bit vector in which each bit represents the processors that
325 // a process is allowed to run on. A system affinity mask is a bit vector in which each bit
326 // represents the processors that are configured into a system.
327 // A process affinity mask is a subset of the system affinity mask. A process is only allowed
328 // to run on the processors configured into a system. Therefore, the process affinity mask cannot
329 // specify a 1 bit for a processor when the system affinity mask specifies a 0 bit for that processor.
330 static bool GetCurrentProcessAffinityMask(uintptr_t *processMask, uintptr_t *systemMask);
331
332 //
333 // Global memory info
334 //
335
336 // Return the size of the user-mode portion of the virtual address space of this process.
337 // Return:
338 // non zero if it has succeeded, 0 if it has failed
339 static size_t GetVirtualMemoryLimit();
340
341 // Get the physical memory that this process can use.
342 // Return:
343 // non zero if it has succeeded, 0 if it has failed
344 // Remarks:
345 // If a process runs with a restricted memory limit, it returns the limit. If there's no limit
346 // specified, it returns amount of actual physical memory.
347 static uint64_t GetPhysicalMemoryLimit();
348
349 // Get memory status
350 // Parameters:
351 // memory_load - A number between 0 and 100 that specifies the approximate percentage of physical memory
352 // that is in use (0 indicates no memory use and 100 indicates full memory use).
353 // available_physical - The amount of physical memory currently available, in bytes.
354 // available_page_file - The maximum amount of memory the current process can commit, in bytes.
355 // Remarks:
356 // Any parameter can be null.
357 static void GetMemoryStatus(uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file);
358
359 // Get size of an OS memory page
360 static uint32_t GetPageSize();
361
362 //
363 // Misc
364 //
365
366 // Flush write buffers of processors that are executing threads of the current process
367 static void FlushProcessWriteBuffers();
368
369 // Break into a debugger
370 static void DebugBreak();
371
372 //
373 // Time
374 //
375
376 // Get a high precision performance counter
377 // Return:
378 // The counter value
379 static int64_t QueryPerformanceCounter();
380
381 // Get a frequency of the high precision performance counter
382 // Return:
383 // The counter frequency
384 static int64_t QueryPerformanceFrequency();
385
386 // Get a time stamp with a low precision
387 // Return:
388 // Time stamp in milliseconds
389 static uint32_t GetLowPrecisionTimeStamp();
390
391 // Gets the total number of processors on the machine, not taking
392 // into account current process affinity.
393 // Return:
394 // Number of processors on the machine
395 static uint32_t GetTotalProcessorCount();
396
397 // Is NUMA support available
398 static bool CanEnableGCNumaAware();
399
400 // Gets the NUMA node for the processor
401 static bool GetNumaProcessorNode(PPROCESSOR_NUMBER proc_no, uint16_t *node_no);
402
403 // Are CPU groups enabled
404 static bool CanEnableGCCPUGroups();
405
406 // Get the CPU group for the specified processor
407 static void GetGroupForProcessor(uint16_t processor_number, uint16_t* group_number, uint16_t* group_processor_number);
408
409};
410
411#endif // __GCENV_OS_H__
412