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 * GCSCAN.CPP
7 *
8 * GC Root Scanning
9 *
10
11 *
12 */
13
14#include "common.h"
15
16#include "gcenv.h"
17
18#include "gcscan.h"
19#include "gc.h"
20#include "objecthandle.h"
21
22VOLATILE(int32_t) GCScan::m_GcStructuresInvalidCnt = 1;
23
24bool GCScan::GetGcRuntimeStructuresValid ()
25{
26 LIMITED_METHOD_CONTRACT;
27 SUPPORTS_DAC;
28 _ASSERTE ((int32_t)m_GcStructuresInvalidCnt >= 0);
29 return (int32_t)m_GcStructuresInvalidCnt == 0;
30}
31
32#ifndef DACCESS_COMPILE
33
34//
35// Dependent handle promotion scan support
36//
37
38// This method is called first during the mark phase. It's job is to set up the context for further scanning
39// (remembering the scan parameters the GC gives us and initializing some state variables we use to determine
40// whether further scans will be required or not).
41//
42// This scan is not guaranteed to return complete results due to the GC context in which we are called. In
43// particular it is possible, due to either a mark stack overflow or unsynchronized operation in server GC
44// mode, that not all reachable objects will be reported as promoted yet. However, the operations we perform
45// will still be correct and this scan allows us to spot a common optimization where no dependent handles are
46// due for retirement in this particular GC. This is an important optimization to take advantage of since
47// synchronizing the GC to calculate complete results is a costly operation.
48void GCScan::GcDhInitialScan(promote_func* fn, int condemned, int max_gen, ScanContext* sc)
49{
50 // We allocate space for dependent handle scanning context during Ref_Initialize. Under server GC there
51 // are actually as many contexts as heaps (and CPUs). Ref_GetDependentHandleContext() retrieves the
52 // correct context for the current GC thread based on the ScanContext passed to us by the GC.
53 DhContext *pDhContext = Ref_GetDependentHandleContext(sc);
54
55 // Record GC callback parameters in the DH context so that the GC doesn't continually have to pass the
56 // same data to each call.
57 pDhContext->m_pfnPromoteFunction = fn;
58 pDhContext->m_iCondemned = condemned;
59 pDhContext->m_iMaxGen = max_gen;
60 pDhContext->m_pScanContext = sc;
61
62 // Look for dependent handle whose primary has been promoted but whose secondary has not. Promote the
63 // secondary in those cases. Additionally this scan sets the m_fUnpromotedPrimaries and m_fPromoted state
64 // flags in the DH context. The m_fUnpromotedPrimaries flag is the most interesting here: if this flag is
65 // false after the scan then it doesn't matter how many object promotions might currently be missing since
66 // there are no secondary objects that are currently unpromoted anyway. This is the (hopefully common)
67 // circumstance under which we don't have to perform any costly additional re-scans.
68 Ref_ScanDependentHandlesForPromotion(pDhContext);
69}
70
71// This method is called after GcDhInitialScan and before each subsequent scan (GcDhReScan below). It
72// determines whether any handles are left that have unpromoted secondaries.
73bool GCScan::GcDhUnpromotedHandlesExist(ScanContext* sc)
74{
75 WRAPPER_NO_CONTRACT;
76 // Locate our dependent handle context based on the GC context.
77 DhContext *pDhContext = Ref_GetDependentHandleContext(sc);
78
79 return pDhContext->m_fUnpromotedPrimaries;
80}
81
82// Perform a re-scan of dependent handles, promoting secondaries associated with newly promoted primaries as
83// above. We may still need to call this multiple times since promotion of a secondary late in the table could
84// promote a primary earlier in the table. Also, GC graph promotions are not guaranteed to be complete by the
85// time the promotion callback returns (the mark stack can overflow). As a result the GC might have to call
86// this method in a loop. The scan records state that let's us know when to terminate (no further handles to
87// be promoted or no promotions in the last scan). Returns true if at least one object was promoted as a
88// result of the scan.
89bool GCScan::GcDhReScan(ScanContext* sc)
90{
91 // Locate our dependent handle context based on the GC context.
92 DhContext *pDhContext = Ref_GetDependentHandleContext(sc);
93
94 return Ref_ScanDependentHandlesForPromotion(pDhContext);
95}
96
97/*
98 * Scan for dead weak pointers
99 */
100
101void GCScan::GcWeakPtrScan( promote_func* fn, int condemned, int max_gen, ScanContext* sc )
102{
103 // Clear out weak pointers that are no longer live.
104 Ref_CheckReachable(condemned, max_gen, (uintptr_t)sc);
105
106 // Clear any secondary objects whose primary object is now definitely dead.
107 Ref_ScanDependentHandlesForClearing(condemned, max_gen, sc, fn);
108}
109
110static void CALLBACK CheckPromoted(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t * /*pExtraInfo*/, uintptr_t /*lp1*/, uintptr_t /*lp2*/)
111{
112 LIMITED_METHOD_CONTRACT;
113
114 LOG((LF_GC, LL_INFO100000, LOG_HANDLE_OBJECT_CLASS("Checking referent of Weak-", pObjRef, "to ", *pObjRef)));
115
116 Object **pRef = (Object **)pObjRef;
117 if (!g_theGCHeap->IsPromoted(*pRef))
118 {
119 LOG((LF_GC, LL_INFO100, LOG_HANDLE_OBJECT_CLASS("Severing Weak-", pObjRef, "to unreachable ", *pObjRef)));
120
121 *pRef = NULL;
122 }
123 else
124 {
125 LOG((LF_GC, LL_INFO1000000, "reachable " LOG_OBJECT_CLASS(*pObjRef)));
126 }
127}
128
129void GCScan::GcWeakPtrScanBySingleThread( int condemned, int max_gen, ScanContext* sc )
130{
131 UNREFERENCED_PARAMETER(condemned);
132 UNREFERENCED_PARAMETER(max_gen);
133 GCToEEInterface::SyncBlockCacheWeakPtrScan(&CheckPromoted, (uintptr_t)sc, 0);
134}
135
136void GCScan::GcScanSizedRefs(promote_func* fn, int condemned, int max_gen, ScanContext* sc)
137{
138 Ref_ScanSizedRefHandles(condemned, max_gen, sc, fn);
139}
140
141void GCScan::GcShortWeakPtrScan(promote_func* fn, int condemned, int max_gen,
142 ScanContext* sc)
143{
144 UNREFERENCED_PARAMETER(fn);
145 Ref_CheckAlive(condemned, max_gen, (uintptr_t)sc);
146}
147
148/*
149 * Scan all stack roots in this 'namespace'
150 */
151
152void GCScan::GcScanRoots(promote_func* fn, int condemned, int max_gen,
153 ScanContext* sc)
154{
155 GCToEEInterface::GcScanRoots(fn, condemned, max_gen, sc);
156}
157
158/*
159 * Scan all handle roots in this 'namespace'
160 */
161
162
163void GCScan::GcScanHandles (promote_func* fn, int condemned, int max_gen,
164 ScanContext* sc)
165{
166 STRESS_LOG1(LF_GC|LF_GCROOTS, LL_INFO10, "GcScanHandles (Promotion Phase = %d)\n", sc->promotion);
167 if (sc->promotion)
168 {
169 Ref_TracePinningRoots(condemned, max_gen, sc, fn);
170 Ref_TraceNormalRoots(condemned, max_gen, sc, fn);
171 }
172 else
173 {
174 Ref_UpdatePointers(condemned, max_gen, sc, fn);
175 Ref_UpdatePinnedPointers(condemned, max_gen, sc, fn);
176 Ref_ScanDependentHandlesForRelocation(condemned, max_gen, sc, fn);
177 }
178}
179
180/*
181 * Scan all handle roots in this 'namespace' for profiling
182 */
183
184void GCScan::GcScanHandlesForProfilerAndETW (int max_gen, ScanContext* sc, handle_scan_fn fn)
185{
186 LIMITED_METHOD_CONTRACT;
187
188#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
189 LOG((LF_GC|LF_GCROOTS, LL_INFO10, "Profiler Root Scan Phase, Handles\n"));
190 Ref_ScanHandlesForProfilerAndETW(max_gen, (uintptr_t)sc, fn);
191#endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
192}
193
194/*
195 * Scan dependent handles in this 'namespace' for profiling
196 */
197void GCScan::GcScanDependentHandlesForProfilerAndETW (int max_gen, ScanContext* sc, handle_scan_fn fn)
198{
199 LIMITED_METHOD_CONTRACT;
200
201#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
202 LOG((LF_GC|LF_GCROOTS, LL_INFO10, "Profiler Root Scan Phase, DependentHandles\n"));
203 Ref_ScanDependentHandlesForProfilerAndETW(max_gen, sc, fn);
204#endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
205}
206
207void GCScan::GcRuntimeStructuresValid (BOOL bValid)
208{
209 WRAPPER_NO_CONTRACT;
210 if (!bValid)
211 {
212 int32_t result;
213 result = Interlocked::Increment (&m_GcStructuresInvalidCnt);
214 _ASSERTE (result > 0);
215 }
216 else
217 {
218 int32_t result;
219 result = Interlocked::Decrement (&m_GcStructuresInvalidCnt);
220 _ASSERTE (result >= 0);
221 }
222}
223
224void GCScan::GcDemote (int condemned, int max_gen, ScanContext* sc)
225{
226 Ref_RejuvenateHandles (condemned, max_gen, (uintptr_t)sc);
227 if (!IsServerHeap() || sc->thread_number == 0)
228 GCToEEInterface::SyncBlockCacheDemote(max_gen);
229}
230
231void GCScan::GcPromotionsGranted (int condemned, int max_gen, ScanContext* sc)
232{
233 Ref_AgeHandles(condemned, max_gen, (uintptr_t)sc);
234 if (!IsServerHeap() || sc->thread_number == 0)
235 GCToEEInterface::SyncBlockCachePromotionsGranted(max_gen);
236}
237
238
239size_t GCScan::AskForMoreReservedMemory (size_t old_size, size_t need_size)
240{
241 LIMITED_METHOD_CONTRACT;
242
243#if !defined(FEATURE_CORECLR) && !defined(FEATURE_REDHAWK)
244 // call the host....
245
246 IGCHostControl *pGCHostControl = CorHost::GetGCHostControl();
247
248 if (pGCHostControl)
249 {
250 size_t new_max_limit_size = need_size;
251 pGCHostControl->RequestVirtualMemLimit (old_size,
252 (SIZE_T*)&new_max_limit_size);
253 return new_max_limit_size;
254 }
255#endif
256
257 return old_size + need_size;
258}
259
260void GCScan::VerifyHandleTable(int condemned, int max_gen, ScanContext* sc)
261{
262 LIMITED_METHOD_CONTRACT;
263 Ref_VerifyHandleTable(condemned, max_gen, sc);
264}
265
266#endif // !DACCESS_COMPILE
267