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.
5 #ifndef _GCHEAPUTILITIES_H_
6 #define _GCHEAPUTILITIES_H_
8 #include "gcinterface.h"
10 // The singular heap instance.
11 GPTR_DECL(IGCHeap, g_pGCHeap);
13 #ifndef DACCESS_COMPILE
15 #endif // !DACCESS_COMPILE
16 GPTR_DECL(uint8_t,g_lowest_address);
17 GPTR_DECL(uint8_t,g_highest_address);
18 GPTR_DECL(uint32_t,g_card_table);
19 GVAL_DECL(GCHeapType, g_heap_type);
20 #ifndef DACCESS_COMPILE
22 #endif // !DACCESS_COMPILE
24 // For single-proc machines, the EE will use a single, shared alloc context
25 // for all allocations. In order to avoid extra indirections in assembly
26 // allocation helpers, the EE owns the global allocation context and the
27 // GC will update it when it needs to.
28 extern "C" gc_alloc_context g_global_alloc_context;
30 extern "C" uint32_t* g_card_bundle_table;
31 extern "C" uint8_t* g_ephemeral_low;
32 extern "C" uint8_t* g_ephemeral_high;
34 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
36 // Table containing the dirty state. This table is translated to exclude the lowest address it represents, see
37 // TranslateTableToExcludeHeapStartAddress.
38 extern "C" uint8_t *g_sw_ww_table;
40 // Write watch may be disabled when it is not needed (between GCs for instance). This indicates whether it is enabled.
41 extern "C" bool g_sw_ww_enabled_for_gc_heap;
43 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
45 // g_gc_dac_vars is a structure of pointers to GC globals that the
46 // DAC uses. It is not exposed directly to the DAC.
47 extern GcDacVars g_gc_dac_vars;
49 // Instead of exposing g_gc_dac_vars to the DAC, a pointer to it
50 // is exposed here (g_gcDacGlobals). The reason for this is to avoid
51 // a problem in which a debugger attaches to a program while the program
52 // is in the middle of initializing the GC DAC vars - if the "publishing"
53 // of DAC vars isn't atomic, the debugger could see a partially initialized
54 // GcDacVars structure.
56 // Instead, the debuggee "publishes" GcDacVars by assigning a pointer to g_gc_dac_vars
57 // to this global, and the DAC will read this global.
58 typedef DPTR(GcDacVars) PTR_GcDacVars;
59 GPTR_DECL(GcDacVars, g_gcDacGlobals);
61 // GCHeapUtilities provides a number of static methods
62 // that operate on the global heap instance. It can't be
64 class GCHeapUtilities {
66 // Retrieves the GC heap.
67 inline static IGCHeap* GetGCHeap()
69 LIMITED_METHOD_CONTRACT;
71 assert(g_pGCHeap != nullptr);
75 // Returns true if the heap has been initialized, false otherwise.
76 inline static bool IsGCHeapInitialized()
78 LIMITED_METHOD_CONTRACT;
80 return g_pGCHeap != nullptr;
83 // Returns true if a the heap is initialized and a garbage collection
84 // is in progress, false otherwise.
85 inline static bool IsGCInProgress(bool bConsiderGCStart = false)
89 return (IsGCHeapInitialized() ? GetGCHeap()->IsGCInProgressHelper(bConsiderGCStart) : false);
92 // Returns true if we should be competing marking for statics. This
93 // influences the behavior of `GCToEEInterface::GcScanRoots`.
94 inline static bool MarkShouldCompeteForStatics()
98 return IsServerHeap() && g_SystemInfo.dwNumberOfProcessors >= 2;
101 // Waits until a GC is complete, if the heap has been initialized.
102 inline static void WaitForGCCompletion(bool bConsiderGCStart = false)
106 if (IsGCHeapInitialized())
107 GetGCHeap()->WaitUntilGCComplete(bConsiderGCStart);
110 // Returns true if the held GC heap is a Server GC heap, false otherwise.
111 inline static bool IsServerHeap()
113 LIMITED_METHOD_CONTRACT;
115 #ifdef FEATURE_SVR_GC
116 _ASSERTE(g_heap_type != GC_HEAP_INVALID);
117 return g_heap_type == GC_HEAP_SVR;
120 #endif // FEATURE_SVR_GC
123 static bool UseThreadAllocationContexts()
125 // When running on a single-proc system, it's more efficient to use a single global
126 // allocation context for SOH allocations than to use one for every thread.
127 #if defined(_TARGET_ARM_) || defined(FEATURE_PAL) || defined(FEATURE_REDHAWK)
130 return IsServerHeap() || ::GetCurrentProcessCpuCount() != 1;
135 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
137 // Returns True if software write watch is currently enabled for the GC Heap,
138 // or False if it is not.
139 inline static bool SoftwareWriteWatchIsEnabled()
143 return g_sw_ww_enabled_for_gc_heap;
146 // In accordance with the SoftwareWriteWatch scheme, marks a given address as
147 // "dirty" (e.g. has been written to).
148 inline static void SoftwareWriteWatchSetDirty(void* address, size_t write_size)
150 LIMITED_METHOD_CONTRACT;
152 // We presumably have just written something to this address, so it can't be null.
153 assert(address != nullptr);
155 // The implementation is limited to writes of a pointer size or less. Writes larger
156 // than pointer size may cross page boundaries and would require us to potentially
157 // set more than one entry in the SWW table, which can't be done atomically under
158 // the current scheme.
159 assert(write_size <= sizeof(void*));
161 size_t table_byte_index = reinterpret_cast<size_t>(address) >> SOFTWARE_WRITE_WATCH_AddressToTableByteIndexShift;
163 // The table byte index that we calculate for the address should be the same as the one
164 // calculated for a pointer to the end of the written region. If this were not the case,
165 // this write crossed a boundary and would dirty two pages.
166 uint8_t* end_of_write_ptr = reinterpret_cast<uint8_t*>(address) + (write_size - 1);
167 assert(table_byte_index == reinterpret_cast<size_t>(end_of_write_ptr) >> SOFTWARE_WRITE_WATCH_AddressToTableByteIndexShift);
168 uint8_t* table_address = &g_sw_ww_table[table_byte_index];
169 if (*table_address == 0)
171 *table_address = 0xFF;
175 // In accordance with the SoftwareWriteWatch scheme, marks a range of addresses
176 // as dirty, starting at the given address and with the given length.
177 inline static void SoftwareWriteWatchSetDirtyRegion(void* address, size_t length)
179 LIMITED_METHOD_CONTRACT;
181 // We presumably have just memcopied something to this address, so it can't be null.
182 assert(address != nullptr);
184 // The "base index" is the first index in the SWW table that covers the target
186 size_t base_index = reinterpret_cast<size_t>(address) >> SOFTWARE_WRITE_WATCH_AddressToTableByteIndexShift;
188 // The "end_index" is the last index in the SWW table that covers the target
190 uint8_t* end_pointer = reinterpret_cast<uint8_t*>(address) + length - 1;
191 size_t end_index = reinterpret_cast<size_t>(end_pointer) >> SOFTWARE_WRITE_WATCH_AddressToTableByteIndexShift;
193 // We'll mark the entire region of memory as dirty by memseting all entries in
194 // the SWW table between the start and end indexes.
195 memset(&g_sw_ww_table[base_index], ~0, end_index - base_index + 1);
197 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
200 // This class should never be instantiated.
201 GCHeapUtilities() = delete;
204 #endif // _GCHEAPUTILITIES_H_