Modify affinity range config format for Windows
[platform/upstream/coreclr.git] / src / gc / env / gcenv.os.h
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 #ifdef HAS_SYSTEM_YIELDPROCESSOR
22 // YieldProcessor is defined to Dont_Use_YieldProcessor. Restore it to the system-default implementation for the GC.
23 #undef YieldProcessor
24 #define YieldProcessor System_YieldProcessor
25 #endif
26
27 #define NUMA_NODE_UNDEFINED UINT16_MAX
28
29 bool ParseIndexOrRange(const char** config_string, size_t* start_index, size_t* end_index);
30
31 // Critical section used by the GC
32 class CLRCriticalSection
33 {
34     CRITICAL_SECTION m_cs;
35
36 public:
37     // Initialize the critical section
38     void Initialize();
39
40     // Destroy the critical section
41     void Destroy();
42
43     // Enter the critical section. Blocks until the section can be entered.
44     void Enter();
45
46     // Leave the critical section
47     void Leave();
48 };
49
50 // Flags for the GCToOSInterface::VirtualReserve method
51 struct VirtualReserveFlags
52 {
53     enum
54     {
55         None = 0,
56         WriteWatch = 1,
57     };
58 };
59
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
62 // the GC.
63 //
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.
71 class GCEvent {
72 private:
73     class Impl;
74     Impl *m_impl;
75
76 public:
77     // Constructs a new uninitialized event.
78     GCEvent();
79
80     // Closes the event. Attempting to use the event past calling CloseEvent
81     // is a logic error.
82     void CloseEvent();
83
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.
88     void Set();
89
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.
94     void Reset();
95
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.
99     // Returns:
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);
105
106     // Determines whether or not this event is valid.
107     // Returns:
108     //  true if this event is invalid (i.e. it has not yet been initialized or
109     //  has already been closed), false otherwise
110     bool IsValid() const
111     {
112         return m_impl != nullptr;
113     }
114
115     // Initializes this event to be a host-aware manual reset event with the
116     // given initial state.
117     // Returns:
118     //   true if the initialization succeeded, false if it did not
119     bool CreateManualEventNoThrow(bool initialState);
120
121     // Initializes this event to be a host-aware auto-resetting event with the
122     // given initial state.
123     // Returns:
124     //   true if the initialization succeeded, false if it did not
125     bool CreateAutoEventNoThrow(bool initialState);
126
127     // Initializes this event to be a host-unaware manual reset event with the
128     // given initial state.
129     // Returns:
130     //   true if the initialization succeeded, false if it did not
131     bool CreateOSManualEventNoThrow(bool initialState);
132
133     // Initializes this event to be a host-unaware auto-resetting event with the
134     // given initial state.
135     // Returns:
136     //   true if the initialization succeeded, false if it did not
137     bool CreateOSAutoEventNoThrow(bool initialState);
138 };
139
140 // GC thread function prototype
141 typedef void (*GCThreadFunction)(void* param);
142
143 #ifdef BIT64
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
147 #else
148 #define MAX_SUPPORTED_CPUS 64
149 #endif // BIT64
150
151 // Add of processor indices used to store affinity.
152 class AffinitySet
153 {
154     static const size_t BitsPerBitsetEntry = 8 * sizeof(uintptr_t);
155
156     uintptr_t m_bitset[MAX_SUPPORTED_CPUS / BitsPerBitsetEntry];
157
158     static uintptr_t GetBitsetEntryMask(size_t cpuIndex)
159     {
160         return (uintptr_t)1 << (cpuIndex & (BitsPerBitsetEntry - 1));
161     }
162
163     static size_t GetBitsetEntryIndex(size_t cpuIndex)
164     {
165         return cpuIndex / BitsPerBitsetEntry;
166     }
167
168 public:
169
170     AffinitySet()
171     {
172         memset(m_bitset, 0, sizeof(m_bitset));
173     }
174
175     // Check if the set contains a processor
176     bool Contains(size_t cpuIndex) const
177     {
178         return (m_bitset[GetBitsetEntryIndex(cpuIndex)] & GetBitsetEntryMask(cpuIndex)) != 0;
179     }
180
181     // Add a processor to the set
182     void Add(size_t cpuIndex)
183     {
184         m_bitset[GetBitsetEntryIndex(cpuIndex)] |= GetBitsetEntryMask(cpuIndex);
185     }
186
187     // Remove a processor from the set
188     void Remove(size_t cpuIndex)
189     {
190         m_bitset[GetBitsetEntryIndex(cpuIndex)] &= ~GetBitsetEntryMask(cpuIndex);
191     }
192
193     // Check if the set is empty
194     bool IsEmpty() const
195     {
196         for (size_t i = 0; i < MAX_SUPPORTED_CPUS / BitsPerBitsetEntry; i++)
197         {
198             if (m_bitset[i] != 0)
199             {
200                 return false;
201             }
202         }
203
204         return true;
205     }
206
207     // Return number of processors in the affinity set
208     size_t Count() const
209     {
210         size_t count = 0;
211         for (size_t i = 0; i < MAX_SUPPORTED_CPUS; i++)
212         {
213             if (Contains(i))
214             {
215                 count++;
216             }
217         }
218
219         return count;
220     }
221 };
222
223 // Interface that the GC uses to invoke OS specific functionality
224 class GCToOSInterface
225 {
226 public:
227
228     //
229     // Initialization and shutdown of the interface
230     //
231
232     // Initialize the interface implementation
233     // Return:
234     //  true if it has succeeded, false if it has failed
235     static bool Initialize();
236
237     // Shutdown the interface implementation
238     static void Shutdown();
239
240     //
241     // Virtual memory management
242     //
243
244     // Reserve virtual memory range.
245     // Parameters:
246     //  size      - size of the virtual memory range
247     //  alignment - requested memory alignment
248     //  flags     - flags to control special settings like write watching
249     // Return:
250     //  Starting virtual address of the reserved range
251     // Notes:
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.
257     //
258     //  Windows guarantees that the returned mapping will be aligned to the allocation
259     //  granularity.
260     static void* VirtualReserve(size_t size, size_t alignment, uint32_t flags);
261
262     // Release virtual memory range previously reserved using VirtualReserve
263     // Parameters:
264     //  address - starting virtual address
265     //  size    - size of the virtual memory range
266     // Return:
267     //  true if it has succeeded, false if it has failed
268     static bool VirtualRelease(void *address, size_t size);
269
270     // Commit virtual memory range. It must be part of a range reserved using VirtualReserve.
271     // Parameters:
272     //  address - starting virtual address
273     //  size    - size of the virtual memory range
274     // Return:
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);
277
278     // Decomit virtual memory range.
279     // Parameters:
280     //  address - starting virtual address
281     //  size    - size of the virtual memory range
282     // Return:
283     //  true if it has succeeded, false if it has failed
284     static bool VirtualDecommit(void *address, size_t size);
285
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.
288     // Parameters:
289     //  address - starting virtual address
290     //  size    - size of the virtual memory range
291     //  unlock  - true if the memory range should also be unlocked
292     // Return:
293     //  true if it has succeeded, false if it has failed
294     static bool VirtualReset(void *address, size_t size, bool unlock);
295
296     //
297     // Write watching
298     //
299
300     // Check if the OS supports write watching
301     static bool SupportsWriteWatch();
302
303     // Reset the write tracking state for the specified virtual memory range.
304     // Parameters:
305     //  address - starting virtual address
306     //  size    - size of the virtual memory range
307     static void ResetWriteWatch(void *address, size_t size);
308
309     // Retrieve addresses of the pages that are written to in a region of virtual memory
310     // Parameters:
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.
317     // Return:
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);
320
321     //
322     // Thread and process
323     //
324
325     // Causes the calling thread to sleep for the specified number of milliseconds
326     // Parameters:
327     //  sleepMSec   - time to sleep before switching to another thread
328     static void Sleep(uint32_t sleepMSec);
329
330     // Causes the calling thread to yield execution to another thread that is ready to run on the current processor.
331     // Parameters:
332     //  switchCount - number of times the YieldThread was called in a loop
333     static void YieldThread(uint32_t switchCount);
334
335     // Get the number of the current processor
336     static uint32_t GetCurrentProcessorNumber();
337
338     // Check if the OS supports getting current processor number
339     static bool CanGetCurrentProcessorNumber();
340
341     // Set ideal processor for the current thread
342     // Parameters:
343     //  srcProcNo - processor number the thread currently runs on
344     //  dstProcNo - processor number the thread should be migrated to
345     // Return:
346     //  true if it has succeeded, false if it has failed
347     static bool SetCurrentThreadIdealAffinity(uint16_t srcProcNo, uint16_t dstProcNo);
348
349     // Get numeric id of the current thread if possible on the
350     // current platform. It is indended for logging purposes only.
351     // Return:
352     //  Numeric id of the current thread or 0 if the 
353     static uint64_t GetCurrentThreadIdForLogging();
354
355     // Get id of the current process
356     // Return:
357     //  Id of the current process
358     static uint32_t GetCurrentProcessId();
359
360     //
361     // Processor topology
362     //
363
364     // Get size of the on die cache per logical processor
365     // Parameters:
366     //  trueSize - true to return true cache size, false to return scaled up size based on
367     //             the processor architecture
368     // Return:
369     //  Size of the cache
370     static size_t GetCacheSizePerLogicalCpu(bool trueSize = true);
371
372     // Get number of processors assigned to the current process
373     // Return:
374     //  The number of processors
375     static uint32_t GetCurrentProcessCpuCount();
376
377     // Sets the calling thread's affinity to only run on the processor specified.
378     // Parameters:
379     //  procNo - The requested affinity for the calling thread.
380     //
381     // Return:
382     //  true if setting the affinity was successful, false otherwise.
383     static bool SetThreadAffinity(uint16_t procNo);
384
385     // Boosts the calling thread's thread priority to a level higher than the default
386     // for new threads.
387     // Parameters:
388     //  None.
389     // Return:
390     //  true if the priority boost was successful, false otherwise.
391     static bool BoostThreadPriority();
392
393     // Set the set of processors enabled for GC threads for the current process based on config specified affinity mask and set
394     // Parameters:
395     //  configAffinityMask - mask specified by the GCHeapAffinitizeMask config
396     //  configAffinitySet - affinity set specified by the GCHeapAffinitizeRanges config
397     // Return:
398     //  set of enabled processors
399     static const AffinitySet* SetGCThreadsAffinitySet(uintptr_t configAffinityMask, const AffinitySet* configAffinitySet);
400
401     //
402     // Global memory info
403     //
404
405     // Return the size of the user-mode portion of the virtual address space of this process.
406     // Return:
407     //  non zero if it has succeeded, 0 if it has failed
408     static size_t GetVirtualMemoryLimit();
409
410     // Get the physical memory that this process can use.
411     // Return:
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.
414     // Remarks:
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.
417     //
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);
421
422     // Get memory status
423     // Parameters:
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.
428     // Remarks:
429     //  Any parameter can be null.
430     static void GetMemoryStatus(uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file);
431
432     // Get size of an OS memory page
433     static uint32_t GetPageSize();
434
435     //
436     // Misc
437     //
438
439     // Flush write buffers of processors that are executing threads of the current process
440     static void FlushProcessWriteBuffers();
441
442     // Break into a debugger
443     static void DebugBreak();
444
445     //
446     // Time
447     //
448
449     // Get a high precision performance counter
450     // Return:
451     //  The counter value
452     static int64_t QueryPerformanceCounter();
453
454     // Get a frequency of the high precision performance counter
455     // Return:
456     //  The counter frequency
457     static int64_t QueryPerformanceFrequency();
458
459     // Get a time stamp with a low precision
460     // Return:
461     //  Time stamp in milliseconds
462     static uint32_t GetLowPrecisionTimeStamp();
463
464     // Gets the total number of processors on the machine, not taking
465     // into account current process affinity.
466     // Return:
467     //  Number of processors on the machine
468     static uint32_t GetTotalProcessorCount();
469
470     // Is NUMA support available
471     static bool CanEnableGCNumaAware();
472
473     // Gets the NUMA node for the processor
474     static bool GetNumaProcessorNode(uint16_t proc_no, uint16_t *node_no);
475
476     // Get processor number and optionally its NUMA node number for the specified heap number
477     // Parameters:
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
481     // Return:
482     //  true if it succeeded
483     static bool GetProcessorForHeap(uint16_t heap_number, uint16_t* proc_no, uint16_t* node_no);
484
485     // Parse the confing string describing affinitization ranges and update the passed in affinitySet accordingly
486     // Parameters:
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
490     // Return:
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);
493
494 };
495
496 #endif // __GCENV_OS_H__