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 * Wraps handle table to implement various handle types (Strong, Weak, etc.)
7 *
8
9 *
10 */
11
12#include "common.h"
13
14#include "gcenv.h"
15
16#include "gc.h"
17#include "gcscan.h"
18
19#include "objecthandle.h"
20#include "handletablepriv.h"
21
22#include "gchandletableimpl.h"
23
24HandleTableMap g_HandleTableMap;
25
26// Array of contexts used while scanning dependent handles for promotion. There are as many contexts as GC
27// heaps and they're allocated by Ref_Initialize and initialized during each GC by GcDhInitialScan.
28DhContext *g_pDependentHandleContexts;
29
30#ifndef DACCESS_COMPILE
31
32//----------------------------------------------------------------------------
33
34/*
35 * struct VARSCANINFO
36 *
37 * used when tracing variable-strength handles.
38 */
39struct VARSCANINFO
40{
41 uintptr_t lEnableMask; // mask of types to trace
42 HANDLESCANPROC pfnTrace; // tracing function to use
43 uintptr_t lp2; // second parameter
44};
45
46
47//----------------------------------------------------------------------------
48
49/*
50 * Scan callback for tracing variable-strength handles.
51 *
52 * This callback is called to trace individual objects referred to by handles
53 * in the variable-strength table.
54 */
55void CALLBACK VariableTraceDispatcher(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pExtraInfo, uintptr_t lp1, uintptr_t lp2)
56{
57 WRAPPER_NO_CONTRACT;
58
59 // lp2 is a pointer to our VARSCANINFO
60 struct VARSCANINFO *pInfo = (struct VARSCANINFO *)lp2;
61
62 // is the handle's dynamic type one we're currently scanning?
63 if ((*pExtraInfo & pInfo->lEnableMask) != 0)
64 {
65 // yes - call the tracing function for this handle
66 pInfo->pfnTrace(pObjRef, NULL, lp1, pInfo->lp2);
67 }
68}
69
70#if defined(FEATURE_COMINTEROP) || defined(FEATURE_REDHAWK)
71/*
72 * Scan callback for tracing ref-counted handles.
73 *
74 * This callback is called to trace individual objects referred to by handles
75 * in the refcounted table.
76 */
77void CALLBACK PromoteRefCounted(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pExtraInfo, uintptr_t lp1, uintptr_t lp2)
78{
79 WRAPPER_NO_CONTRACT;
80 UNREFERENCED_PARAMETER(pExtraInfo);
81
82 // there are too many races when asychnronously scanning ref-counted handles so we no longer support it
83 _ASSERTE(!((ScanContext*)lp1)->concurrent);
84
85 LOG((LF_GC, LL_INFO1000, LOG_HANDLE_OBJECT_CLASS("", pObjRef, "causes promotion of ", *pObjRef)));
86
87 Object *pObj = VolatileLoad((PTR_Object*)pObjRef);
88
89#ifdef _DEBUG
90 Object *pOldObj = pObj;
91#endif
92
93 if (!HndIsNullOrDestroyedHandle(pObj) && !g_theGCHeap->IsPromoted(pObj))
94 {
95 if (GCToEEInterface::RefCountedHandleCallbacks(pObj))
96 {
97 _ASSERTE(lp2);
98 promote_func* callback = (promote_func*) lp2;
99 callback(&pObj, (ScanContext *)lp1, 0);
100 }
101 }
102
103 // Assert this object wasn't relocated since we are passing a temporary object's address.
104 _ASSERTE(pOldObj == pObj);
105}
106#endif // FEATURE_COMINTEROP || FEATURE_REDHAWK
107
108
109// Only used by profiling/ETW.
110//----------------------------------------------------------------------------
111
112/*
113 * struct DIAG_DEPSCANINFO
114 *
115 * used when tracing dependent handles for profiling/ETW.
116 */
117struct DIAG_DEPSCANINFO
118{
119 HANDLESCANPROC pfnTrace; // tracing function to use
120 uintptr_t pfnProfilingOrETW;
121};
122
123void CALLBACK TraceDependentHandle(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pExtraInfo, uintptr_t lp1, uintptr_t lp2)
124{
125 WRAPPER_NO_CONTRACT;
126
127 if (pObjRef == NULL || pExtraInfo == NULL)
128 return;
129
130 // At this point, it's possible that either or both of the primary and secondary
131 // objects are NULL. However, if the secondary object is non-NULL, then the primary
132 // object should also be non-NULL.
133 _ASSERTE(*pExtraInfo == 0 || *pObjRef != NULL);
134
135 struct DIAG_DEPSCANINFO *pInfo = (struct DIAG_DEPSCANINFO*)lp2;
136
137 HANDLESCANPROC pfnTrace = pInfo->pfnTrace;
138
139 // is the handle's secondary object non-NULL?
140 if ((*pObjRef != NULL) && (*pExtraInfo != 0))
141 {
142 // yes - call the tracing function for this handle
143 pfnTrace(pObjRef, NULL, lp1, (uintptr_t)(pInfo->pfnProfilingOrETW));
144 }
145}
146
147void CALLBACK UpdateDependentHandle(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pExtraInfo, uintptr_t lp1, uintptr_t lp2)
148{
149 LIMITED_METHOD_CONTRACT;
150 _ASSERTE(pExtraInfo);
151
152 Object **pPrimaryRef = (Object **)pObjRef;
153 Object **pSecondaryRef = (Object **)pExtraInfo;
154
155 LOG((LF_GC|LF_ENC, LL_INFO10000, LOG_HANDLE_OBJECT("Querying for new location of ",
156 pPrimaryRef, "to ", *pPrimaryRef)));
157 LOG((LF_GC|LF_ENC, LL_INFO10000, LOG_HANDLE_OBJECT(" and ",
158 pSecondaryRef, "to ", *pSecondaryRef)));
159
160#ifdef _DEBUG
161 Object *pOldPrimary = *pPrimaryRef;
162 Object *pOldSecondary = *pSecondaryRef;
163#endif
164
165 _ASSERTE(lp2);
166 promote_func* callback = (promote_func*) lp2;
167 callback(pPrimaryRef, (ScanContext *)lp1, 0);
168 callback(pSecondaryRef, (ScanContext *)lp1, 0);
169
170#ifdef _DEBUG
171 if (pOldPrimary != *pPrimaryRef)
172 LOG((LF_GC|LF_ENC, LL_INFO10000, "Updating " FMT_HANDLE "from" FMT_ADDR "to " FMT_OBJECT "\n",
173 DBG_ADDR(pPrimaryRef), DBG_ADDR(pOldPrimary), DBG_ADDR(*pPrimaryRef)));
174 else
175 LOG((LF_GC|LF_ENC, LL_INFO10000, "Updating " FMT_HANDLE "- " FMT_OBJECT "did not move\n",
176 DBG_ADDR(pPrimaryRef), DBG_ADDR(*pPrimaryRef)));
177 if (pOldSecondary != *pSecondaryRef)
178 LOG((LF_GC|LF_ENC, LL_INFO10000, "Updating " FMT_HANDLE "from" FMT_ADDR "to " FMT_OBJECT "\n",
179 DBG_ADDR(pSecondaryRef), DBG_ADDR(pOldSecondary), DBG_ADDR(*pSecondaryRef)));
180 else
181 LOG((LF_GC|LF_ENC, LL_INFO10000, "Updating " FMT_HANDLE "- " FMT_OBJECT "did not move\n",
182 DBG_ADDR(pSecondaryRef), DBG_ADDR(*pSecondaryRef)));
183#endif
184}
185
186void CALLBACK PromoteDependentHandle(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pExtraInfo, uintptr_t lp1, uintptr_t lp2)
187{
188 LIMITED_METHOD_CONTRACT;
189 _ASSERTE(pExtraInfo);
190
191 Object **pPrimaryRef = (Object **)pObjRef;
192 Object **pSecondaryRef = (Object **)pExtraInfo;
193 LOG((LF_GC|LF_ENC, LL_INFO1000, "Checking promotion of DependentHandle"));
194 LOG((LF_GC|LF_ENC, LL_INFO1000, LOG_HANDLE_OBJECT_CLASS("\tPrimary:\t", pObjRef, "to ", *pObjRef)));
195 LOG((LF_GC|LF_ENC, LL_INFO1000, LOG_HANDLE_OBJECT_CLASS("\tSecondary\t", pSecondaryRef, "to ", *pSecondaryRef)));
196
197 ScanContext *sc = (ScanContext*)lp1;
198 DhContext *pDhContext = Ref_GetDependentHandleContext(sc);
199
200 if (*pObjRef && g_theGCHeap->IsPromoted(*pPrimaryRef))
201 {
202 if (!g_theGCHeap->IsPromoted(*pSecondaryRef))
203 {
204 LOG((LF_GC|LF_ENC, LL_INFO10000, "\tPromoting secondary " LOG_OBJECT_CLASS(*pSecondaryRef)));
205 _ASSERTE(lp2);
206 promote_func* callback = (promote_func*) lp2;
207 callback(pSecondaryRef, (ScanContext *)lp1, 0);
208 // need to rescan because we might have promoted an object that itself has added fields and this
209 // promotion might be all that is pinning that object. If we've already scanned that dependent
210 // handle relationship, we could lose it secondary object.
211 pDhContext->m_fPromoted = true;
212 }
213 }
214 else if (*pObjRef)
215 {
216 // If we see a non-cleared primary which hasn't been promoted, record the fact. We will only require a
217 // rescan if this flag has been set (if it's clear then the previous scan found only clear and
218 // promoted handles, so there's no chance of finding an additional handle being promoted on a
219 // subsequent scan).
220 pDhContext->m_fUnpromotedPrimaries = true;
221 }
222}
223
224void CALLBACK ClearDependentHandle(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pExtraInfo, uintptr_t /*lp1*/, uintptr_t /*lp2*/)
225{
226 LIMITED_METHOD_CONTRACT;
227 _ASSERTE(pExtraInfo);
228
229 Object **pPrimaryRef = (Object **)pObjRef;
230 Object **pSecondaryRef = (Object **)pExtraInfo;
231 LOG((LF_GC|LF_ENC, LL_INFO1000, "Checking referent of DependentHandle"));
232 LOG((LF_GC|LF_ENC, LL_INFO1000, LOG_HANDLE_OBJECT_CLASS("\tPrimary:\t", pPrimaryRef, "to ", *pPrimaryRef)));
233 LOG((LF_GC|LF_ENC, LL_INFO1000, LOG_HANDLE_OBJECT_CLASS("\tSecondary\t", pSecondaryRef, "to ", *pSecondaryRef)));
234
235 if (!g_theGCHeap->IsPromoted(*pPrimaryRef))
236 {
237 LOG((LF_GC|LF_ENC, LL_INFO1000, "\tunreachable ", LOG_OBJECT_CLASS(*pPrimaryRef)));
238 LOG((LF_GC|LF_ENC, LL_INFO1000, "\tunreachable ", LOG_OBJECT_CLASS(*pSecondaryRef)));
239 *pPrimaryRef = NULL;
240 *pSecondaryRef = NULL;
241 }
242 else
243 {
244 _ASSERTE(g_theGCHeap->IsPromoted(*pSecondaryRef));
245 LOG((LF_GC|LF_ENC, LL_INFO10000, "\tPrimary is reachable " LOG_OBJECT_CLASS(*pPrimaryRef)));
246 LOG((LF_GC|LF_ENC, LL_INFO10000, "\tSecondary is reachable " LOG_OBJECT_CLASS(*pSecondaryRef)));
247 }
248}
249
250/*
251 * Scan callback for pinning handles.
252 *
253 * This callback is called to pin individual objects referred to by handles in
254 * the pinning table.
255 */
256void CALLBACK PinObject(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pExtraInfo, uintptr_t lp1, uintptr_t lp2)
257{
258 STATIC_CONTRACT_NOTHROW;
259 STATIC_CONTRACT_GC_NOTRIGGER;
260 STATIC_CONTRACT_SO_TOLERANT;
261 STATIC_CONTRACT_MODE_COOPERATIVE;
262 UNREFERENCED_PARAMETER(pExtraInfo);
263
264 // PINNING IS BAD - DON'T DO IT IF YOU CAN AVOID IT
265 LOG((LF_GC, LL_WARNING, LOG_HANDLE_OBJECT_CLASS("WARNING: ", pObjRef, "causes pinning of ", *pObjRef)));
266
267 Object **pRef = (Object **)pObjRef;
268 _ASSERTE(lp2);
269 promote_func* callback = (promote_func*) lp2;
270 callback(pRef, (ScanContext *)lp1, GC_CALL_PINNED);
271}
272
273void CALLBACK AsyncPinObject(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pExtraInfo, uintptr_t lp1, uintptr_t lp2)
274{
275 UNREFERENCED_PARAMETER(pExtraInfo);
276
277 LOG((LF_GC, LL_WARNING, LOG_HANDLE_OBJECT_CLASS("WARNING: ", pObjRef, "causes (async) pinning of ", *pObjRef)));
278
279 Object **pRef = (Object **)pObjRef;
280 _ASSERTE(lp2);
281 promote_func* callback = (promote_func*)lp2;
282 callback(pRef, (ScanContext *)lp1, 0);
283 Object* pPinnedObj = *pRef;
284 if (!HndIsNullOrDestroyedHandle(pPinnedObj))
285 {
286 GCToEEInterface::WalkAsyncPinnedForPromotion(pPinnedObj, (ScanContext *)lp1, callback);
287 }
288}
289
290
291/*
292 * Scan callback for tracing strong handles.
293 *
294 * This callback is called to trace individual objects referred to by handles
295 * in the strong table.
296 */
297void CALLBACK PromoteObject(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pExtraInfo, uintptr_t lp1, uintptr_t lp2)
298{
299 WRAPPER_NO_CONTRACT;
300 UNREFERENCED_PARAMETER(pExtraInfo);
301
302 LOG((LF_GC, LL_INFO1000, LOG_HANDLE_OBJECT_CLASS("", pObjRef, "causes promotion of ", *pObjRef)));
303
304 Object **ppRef = (Object **)pObjRef;
305 _ASSERTE(lp2);
306 promote_func* callback = (promote_func*) lp2;
307 callback(ppRef, (ScanContext *)lp1, 0);
308}
309
310
311/*
312 * Scan callback for disconnecting dead handles.
313 *
314 * This callback is called to check promotion of individual objects referred to by
315 * handles in the weak tables.
316 */
317void CALLBACK CheckPromoted(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pExtraInfo, uintptr_t lp1, uintptr_t lp2)
318{
319 WRAPPER_NO_CONTRACT;
320 UNREFERENCED_PARAMETER(pExtraInfo);
321 UNREFERENCED_PARAMETER(lp1);
322 UNREFERENCED_PARAMETER(lp2);
323
324 LOG((LF_GC, LL_INFO100000, LOG_HANDLE_OBJECT_CLASS("Checking referent of Weak-", pObjRef, "to ", *pObjRef)));
325
326 Object **ppRef = (Object **)pObjRef;
327 if (!g_theGCHeap->IsPromoted(*ppRef))
328 {
329 LOG((LF_GC, LL_INFO100, LOG_HANDLE_OBJECT_CLASS("Severing Weak-", pObjRef, "to unreachable ", *pObjRef)));
330
331 *ppRef = NULL;
332 }
333 else
334 {
335 LOG((LF_GC, LL_INFO1000000, "reachable " LOG_OBJECT_CLASS(*pObjRef)));
336 }
337}
338
339void CALLBACK CalculateSizedRefSize(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pExtraInfo, uintptr_t lp1, uintptr_t lp2)
340{
341 LIMITED_METHOD_CONTRACT;
342
343 _ASSERTE(pExtraInfo);
344
345 Object **ppSizedRef = (Object **)pObjRef;
346 size_t* pSize = (size_t *)pExtraInfo;
347 LOG((LF_GC, LL_INFO100000, LOG_HANDLE_OBJECT_CLASS("Getting size of referent of SizedRef-", pObjRef, "to ", *pObjRef)));
348
349 ScanContext* sc = (ScanContext *)lp1;
350 promote_func* callback = (promote_func*) lp2;
351
352 size_t sizeBegin = g_theGCHeap->GetPromotedBytes(sc->thread_number);
353 callback(ppSizedRef, (ScanContext *)lp1, 0);
354 size_t sizeEnd = g_theGCHeap->GetPromotedBytes(sc->thread_number);
355 *pSize = sizeEnd - sizeBegin;
356}
357
358/*
359 * Scan callback for updating pointers.
360 *
361 * This callback is called to update pointers for individual objects referred to by
362 * handles in the weak and strong tables.
363 */
364void CALLBACK UpdatePointer(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pExtraInfo, uintptr_t lp1, uintptr_t lp2)
365{
366 LIMITED_METHOD_CONTRACT;
367 UNREFERENCED_PARAMETER(pExtraInfo);
368
369 LOG((LF_GC, LL_INFO100000, LOG_HANDLE_OBJECT("Querying for new location of ", pObjRef, "to ", *pObjRef)));
370
371 Object **ppRef = (Object **)pObjRef;
372
373#ifdef _DEBUG
374 Object *pOldLocation = *ppRef;
375#endif
376
377 _ASSERTE(lp2);
378 promote_func* callback = (promote_func*) lp2;
379 callback(ppRef, (ScanContext *)lp1, 0);
380
381#ifdef _DEBUG
382 if (pOldLocation != *pObjRef)
383 LOG((LF_GC, LL_INFO10000, "Updating " FMT_HANDLE "from" FMT_ADDR "to " FMT_OBJECT "\n",
384 DBG_ADDR(pObjRef), DBG_ADDR(pOldLocation), DBG_ADDR(*pObjRef)));
385 else
386 LOG((LF_GC, LL_INFO100000, "Updating " FMT_HANDLE "- " FMT_OBJECT "did not move\n",
387 DBG_ADDR(pObjRef), DBG_ADDR(*pObjRef)));
388#endif
389}
390
391
392#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
393/*
394 * Scan callback for updating pointers.
395 *
396 * This callback is called to update pointers for individual objects referred to by
397 * handles in the weak and strong tables.
398 */
399void CALLBACK ScanPointerForProfilerAndETW(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pExtraInfo, uintptr_t lp1, uintptr_t lp2)
400{
401 CONTRACTL
402 {
403 NOTHROW;
404 GC_NOTRIGGER;
405 }
406 CONTRACTL_END;
407 UNREFERENCED_PARAMETER(pExtraInfo);
408 handle_scan_fn fn = (handle_scan_fn)lp2;
409
410 LOG((LF_GC | LF_CORPROF, LL_INFO100000, LOG_HANDLE_OBJECT_CLASS("Notifying profiler of ", pObjRef, "to ", *pObjRef)));
411
412 // Get the baseobject (which can subsequently be cast into an OBJECTREF == ObjectID
413 Object **pRef = (Object **)pObjRef;
414
415 // Get a hold of the heap ID that's tacked onto the end of the scancontext struct.
416 ScanContext *pSC = (ScanContext *)lp1;
417
418 uint32_t rootFlags = 0;
419 bool isDependent = false;
420
421 OBJECTHANDLE handle = (OBJECTHANDLE)(pRef);
422 switch (HandleFetchType(handle))
423 {
424 case HNDTYPE_DEPENDENT:
425 isDependent = true;
426 break;
427 case HNDTYPE_WEAK_SHORT:
428 case HNDTYPE_WEAK_LONG:
429#ifdef FEATURE_COMINTEROP
430 case HNDTYPE_WEAK_WINRT:
431#endif // FEATURE_COMINTEROP
432 rootFlags |= kEtwGCRootFlagsWeakRef;
433 break;
434
435 case HNDTYPE_STRONG:
436 case HNDTYPE_SIZEDREF:
437 break;
438
439 case HNDTYPE_PINNED:
440 case HNDTYPE_ASYNCPINNED:
441 rootFlags |= kEtwGCRootFlagsPinning;
442 break;
443
444 case HNDTYPE_VARIABLE:
445#ifdef FEATURE_REDHAWK
446 {
447 // Set the appropriate ETW flags for the current strength of this variable handle
448 uint32_t nVarHandleType = GetVariableHandleType(handle);
449 if (((nVarHandleType & VHT_WEAK_SHORT) != 0) ||
450 ((nVarHandleType & VHT_WEAK_LONG) != 0))
451 {
452 rootFlags |= kEtwGCRootFlagsWeakRef;
453 }
454 if ((nVarHandleType & VHT_PINNED) != 0)
455 {
456 rootFlags |= kEtwGCRootFlagsPinning;
457 }
458
459 // No special ETW flag for strong handles (VHT_STRONG)
460 }
461#else
462 _ASSERTE(!"Variable handle encountered");
463#endif
464 break;
465
466#if defined(FEATURE_COMINTEROP) && !defined(FEATURE_REDHAWK)
467 case HNDTYPE_REFCOUNTED:
468 rootFlags |= kEtwGCRootFlagsRefCounted;
469 if (*pRef != NULL)
470 {
471 if (!GCToEEInterface::RefCountedHandleCallbacks(*pRef))
472 rootFlags |= kEtwGCRootFlagsWeakRef;
473 }
474 break;
475#endif // FEATURE_COMINTEROP || FEATURE_REDHAWK
476 }
477
478 _UNCHECKED_OBJECTREF pSec = NULL;
479
480 if (isDependent)
481 {
482 pSec = (_UNCHECKED_OBJECTREF)HndGetHandleExtraInfo(handle);
483 }
484
485 fn(pRef, pSec, rootFlags, pSC, isDependent);
486}
487#endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
488
489/*
490 * Scan callback for updating pointers.
491 *
492 * This callback is called to update pointers for individual objects referred to by
493 * handles in the pinned table.
494 */
495void CALLBACK UpdatePointerPinned(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pExtraInfo, uintptr_t lp1, uintptr_t lp2)
496{
497 LIMITED_METHOD_CONTRACT;
498 UNREFERENCED_PARAMETER(pExtraInfo);
499
500 Object **ppRef = (Object **)pObjRef;
501
502 _ASSERTE(lp2);
503 promote_func* callback = (promote_func*) lp2;
504 callback(ppRef, (ScanContext *)lp1, GC_CALL_PINNED);
505
506 LOG((LF_GC, LL_INFO100000, LOG_HANDLE_OBJECT("Updating ", pObjRef, "to pinned ", *pObjRef)));
507}
508
509
510//----------------------------------------------------------------------------
511
512// flags describing the handle types
513static const uint32_t s_rgTypeFlags[] =
514{
515 HNDF_NORMAL, // HNDTYPE_WEAK_SHORT
516 HNDF_NORMAL, // HNDTYPE_WEAK_LONG
517 HNDF_NORMAL, // HNDTYPE_STRONG
518 HNDF_NORMAL, // HNDTYPE_PINNED
519 HNDF_EXTRAINFO, // HNDTYPE_VARIABLE
520 HNDF_NORMAL, // HNDTYPE_REFCOUNTED
521 HNDF_EXTRAINFO, // HNDTYPE_DEPENDENT
522 HNDF_NORMAL, // HNDTYPE_ASYNCPINNED
523 HNDF_EXTRAINFO, // HNDTYPE_SIZEDREF
524 HNDF_EXTRAINFO, // HNDTYPE_WEAK_WINRT
525};
526
527int getNumberOfSlots()
528{
529 WRAPPER_NO_CONTRACT;
530
531 // when Ref_Initialize called, IGCHeap::GetNumberOfHeaps() is still 0, so use #procs as a workaround
532 // it is legal since even if later #heaps < #procs we create handles by thread home heap
533 // and just have extra unused slots in HandleTableBuckets, which does not take a lot of space
534 if (!IsServerHeap())
535 return 1;
536
537 return GCToOSInterface::GetCurrentProcessCpuCount();
538}
539
540class HandleTableBucketHolder
541{
542private:
543 HandleTableBucket* m_bucket;
544 int m_slots;
545 BOOL m_SuppressRelease;
546public:
547 HandleTableBucketHolder(HandleTableBucket* bucket, int slots);
548 ~HandleTableBucketHolder();
549
550 void SuppressRelease()
551 {
552 m_SuppressRelease = TRUE;
553 }
554};
555
556HandleTableBucketHolder::HandleTableBucketHolder(HandleTableBucket* bucket, int slots)
557 :m_bucket(bucket), m_slots(slots), m_SuppressRelease(FALSE)
558{
559}
560
561HandleTableBucketHolder::~HandleTableBucketHolder()
562{
563 if (m_SuppressRelease)
564 {
565 return;
566 }
567 if (m_bucket->pTable)
568 {
569 for (int n = 0; n < m_slots; n ++)
570 {
571 if (m_bucket->pTable[n])
572 {
573 HndDestroyHandleTable(m_bucket->pTable[n]);
574 }
575 }
576 delete [] m_bucket->pTable;
577 }
578
579 // we do not own m_bucket, so we shouldn't delete it here.
580}
581
582bool Ref_Initialize()
583{
584 CONTRACTL
585 {
586 NOTHROW;
587 WRAPPER(GC_NOTRIGGER);
588 INJECT_FAULT(return false);
589 }
590 CONTRACTL_END;
591
592 // sanity
593 _ASSERTE(g_HandleTableMap.pBuckets == NULL);
594
595 // Create an array of INITIAL_HANDLE_TABLE_ARRAY_SIZE HandleTableBuckets to hold the handle table sets
596 HandleTableBucket** pBuckets = new (nothrow) HandleTableBucket * [ INITIAL_HANDLE_TABLE_ARRAY_SIZE ];
597 if (pBuckets == NULL)
598 return false;
599
600 ZeroMemory(pBuckets, INITIAL_HANDLE_TABLE_ARRAY_SIZE * sizeof (HandleTableBucket *));
601
602 g_gcGlobalHandleStore = new (nothrow) GCHandleStore();
603 if (g_gcGlobalHandleStore == NULL)
604 {
605 delete[] pBuckets;
606 return false;
607 }
608
609 // Initialize the bucket in the global handle store
610 HandleTableBucket* pBucket = &g_gcGlobalHandleStore->_underlyingBucket;
611
612 pBucket->HandleTableIndex = 0;
613
614 int n_slots = getNumberOfSlots();
615
616 HandleTableBucketHolder bucketHolder(pBucket, n_slots);
617
618 // create the handle table set for the first bucket
619 pBucket->pTable = new (nothrow) HHANDLETABLE[n_slots];
620 if (pBucket->pTable == NULL)
621 goto CleanupAndFail;
622
623 ZeroMemory(pBucket->pTable,
624 n_slots * sizeof(HHANDLETABLE));
625 for (int uCPUindex = 0; uCPUindex < n_slots; uCPUindex++)
626 {
627 pBucket->pTable[uCPUindex] = HndCreateHandleTable(s_rgTypeFlags, _countof(s_rgTypeFlags), ADIndex(1));
628 if (pBucket->pTable[uCPUindex] == NULL)
629 goto CleanupAndFail;
630
631 HndSetHandleTableIndex(pBucket->pTable[uCPUindex], 0);
632 }
633
634 pBuckets[0] = pBucket;
635 bucketHolder.SuppressRelease();
636
637 g_HandleTableMap.pBuckets = pBuckets;
638 g_HandleTableMap.dwMaxIndex = INITIAL_HANDLE_TABLE_ARRAY_SIZE;
639 g_HandleTableMap.pNext = NULL;
640
641 // Allocate contexts used during dependent handle promotion scanning. There's one of these for every GC
642 // heap since they're scanned in parallel.
643 g_pDependentHandleContexts = new (nothrow) DhContext[n_slots];
644 if (g_pDependentHandleContexts == NULL)
645 goto CleanupAndFail;
646
647 return true;
648
649CleanupAndFail:
650 if (pBuckets != NULL)
651 delete[] pBuckets;
652
653 if (g_gcGlobalHandleStore != NULL)
654 delete g_gcGlobalHandleStore;
655
656 return false;
657}
658
659void Ref_Shutdown()
660{
661 WRAPPER_NO_CONTRACT;
662
663 if (g_pDependentHandleContexts)
664 {
665 delete [] g_pDependentHandleContexts;
666 g_pDependentHandleContexts = NULL;
667 }
668
669 // are there any handle tables?
670 if (g_HandleTableMap.pBuckets)
671 {
672 // don't destroy any of the indexed handle tables; they should
673 // be destroyed externally.
674
675 // destroy the handle table bucket array
676 HandleTableMap *walk = &g_HandleTableMap;
677 while (walk) {
678 delete [] walk->pBuckets;
679 walk = walk->pNext;
680 }
681
682 // null out the handle table array
683 g_HandleTableMap.pNext = NULL;
684 g_HandleTableMap.dwMaxIndex = 0;
685
686 // null out the global table handle
687 g_HandleTableMap.pBuckets = NULL;
688 }
689}
690
691#ifndef FEATURE_REDHAWK
692bool Ref_InitializeHandleTableBucket(HandleTableBucket* bucket, void* context)
693{
694 CONTRACTL
695 {
696 NOTHROW;
697 WRAPPER(GC_TRIGGERS);
698 INJECT_FAULT(return false);
699 }
700 CONTRACTL_END;
701
702 HandleTableBucket *result = bucket;
703 HandleTableMap *walk = &g_HandleTableMap;
704
705 HandleTableMap *last = NULL;
706 uint32_t offset = 0;
707
708 result->pTable = NULL;
709
710 // create handle table set for the bucket
711 int n_slots = getNumberOfSlots();
712
713 HandleTableBucketHolder bucketHolder(result, n_slots);
714
715 result->pTable = new (nothrow) HHANDLETABLE[n_slots];
716 if (!result->pTable)
717 {
718 return false;
719 }
720
721 ZeroMemory(result->pTable, n_slots * sizeof(HHANDLETABLE));
722
723 for (int uCPUindex=0; uCPUindex < n_slots; uCPUindex++) {
724 result->pTable[uCPUindex] = HndCreateHandleTable(s_rgTypeFlags, _countof(s_rgTypeFlags), ADIndex((DWORD)(uintptr_t)context));
725 if (!result->pTable[uCPUindex])
726 return false;
727 }
728
729 for (;;) {
730 // Do we have free slot
731 while (walk) {
732 for (uint32_t i = 0; i < INITIAL_HANDLE_TABLE_ARRAY_SIZE; i ++) {
733 if (walk->pBuckets[i] == 0) {
734 for (int uCPUindex=0; uCPUindex < n_slots; uCPUindex++)
735 HndSetHandleTableIndex(result->pTable[uCPUindex], i+offset);
736
737 result->HandleTableIndex = i+offset;
738 if (Interlocked::CompareExchangePointer(&walk->pBuckets[i], result, NULL) == 0) {
739 // Get a free slot.
740 bucketHolder.SuppressRelease();
741 return true;
742 }
743 }
744 }
745 last = walk;
746 offset = walk->dwMaxIndex;
747 walk = walk->pNext;
748 }
749
750 // No free slot.
751 // Let's create a new node
752 HandleTableMap *newMap = new (nothrow) HandleTableMap;
753 if (!newMap)
754 {
755 return false;
756 }
757
758 newMap->pBuckets = new (nothrow) HandleTableBucket * [ INITIAL_HANDLE_TABLE_ARRAY_SIZE ];
759 if (!newMap->pBuckets)
760 {
761 delete newMap;
762 return false;
763 }
764
765 newMap->dwMaxIndex = last->dwMaxIndex + INITIAL_HANDLE_TABLE_ARRAY_SIZE;
766 newMap->pNext = NULL;
767 ZeroMemory(newMap->pBuckets,
768 INITIAL_HANDLE_TABLE_ARRAY_SIZE * sizeof (HandleTableBucket *));
769
770 if (Interlocked::CompareExchangePointer(&last->pNext, newMap, NULL) != NULL)
771 {
772 // This thread loses.
773 delete [] newMap->pBuckets;
774 delete newMap;
775 }
776 walk = last->pNext;
777 offset = last->dwMaxIndex;
778 }
779}
780#endif // !FEATURE_REDHAWK
781
782void Ref_RemoveHandleTableBucket(HandleTableBucket *pBucket)
783{
784 LIMITED_METHOD_CONTRACT;
785
786 size_t index = pBucket->HandleTableIndex;
787 HandleTableMap* walk = &g_HandleTableMap;
788 size_t offset = 0;
789
790 while (walk)
791 {
792 if ((index < walk->dwMaxIndex) && (index >= offset))
793 {
794 // During AppDomain unloading, we first remove a handle table and then destroy
795 // the table. As soon as the table is removed, the slot can be reused.
796 if (walk->pBuckets[index - offset] == pBucket)
797 {
798 walk->pBuckets[index - offset] = NULL;
799 return;
800 }
801 }
802 offset = walk->dwMaxIndex;
803 walk = walk->pNext;
804 }
805
806 // Didn't find it. This will happen typically from Ref_DestroyHandleTableBucket if
807 // we explicitly call Ref_RemoveHandleTableBucket first.
808}
809
810
811void Ref_DestroyHandleTableBucket(HandleTableBucket *pBucket)
812{
813 WRAPPER_NO_CONTRACT;
814
815 Ref_RemoveHandleTableBucket(pBucket);
816 for (int uCPUindex=0; uCPUindex < getNumberOfSlots(); uCPUindex++)
817 {
818 HndDestroyHandleTable(pBucket->pTable[uCPUindex]);
819 }
820 delete [] pBucket->pTable;
821}
822
823int getSlotNumber(ScanContext* sc)
824{
825 WRAPPER_NO_CONTRACT;
826
827 return (IsServerHeap() ? sc->thread_number : 0);
828}
829
830// <TODO> - reexpress as complete only like hndtable does now!!! -fmh</REVISIT_TODO>
831void Ref_EndSynchronousGC(uint32_t condemned, uint32_t maxgen)
832{
833 LIMITED_METHOD_CONTRACT;
834 UNREFERENCED_PARAMETER(condemned);
835 UNREFERENCED_PARAMETER(maxgen);
836
837// NOT used, must be modified for MTHTS (scalable HandleTable scan) if planned to use:
838// need to pass ScanContext info to split HT bucket by threads, or to be performed under t_join::join
839/*
840 // tell the table we finished a GC
841 HandleTableMap *walk = &g_HandleTableMap;
842 while (walk) {
843 for (uint32_t i = 0; i < INITIAL_HANDLE_TABLE_ARRAY_SIZE; i ++) {
844 HHANDLETABLE hTable = walk->pTable[i];
845 if (hTable)
846 HndNotifyGcCycleComplete(hTable, condemned, maxgen);
847 }
848 walk = walk->pNext;
849 }
850*/
851}
852
853void SetDependentHandleSecondary(OBJECTHANDLE handle, OBJECTREF objref)
854{
855 CONTRACTL
856 {
857 NOTHROW;
858 GC_NOTRIGGER;
859 SO_TOLERANT;
860 MODE_COOPERATIVE;
861 }
862 CONTRACTL_END;
863
864 // sanity
865 _ASSERTE(handle);
866
867#ifdef _DEBUG
868 // handle should not be in unloaded domain
869 ValidateAppDomainForHandle(handle);
870
871 // Make sure the objref is valid before it is assigned to a handle
872 ValidateAssignObjrefForHandle(objref, HndGetHandleTableADIndex(HndGetHandleTable(handle)));
873#endif
874 // unwrap the objectref we were given
875 _UNCHECKED_OBJECTREF value = OBJECTREF_TO_UNCHECKED_OBJECTREF(objref);
876
877 // if we are doing a non-NULL pointer store then invoke the write-barrier
878 if (value)
879 HndWriteBarrier(handle, objref);
880
881 // store the pointer
882 HndSetHandleExtraInfo(handle, HNDTYPE_DEPENDENT, (uintptr_t)value);
883}
884
885
886//----------------------------------------------------------------------------
887
888/*
889* GetVariableHandleType.
890*
891* Retrieves the dynamic type of a variable-strength handle.
892*/
893uint32_t GetVariableHandleType(OBJECTHANDLE handle)
894{
895 WRAPPER_NO_CONTRACT;
896
897 return (uint32_t)HndGetHandleExtraInfo(handle);
898}
899
900/*
901 * UpdateVariableHandleType.
902 *
903 * Changes the dynamic type of a variable-strength handle.
904 *
905 * N.B. This routine is not a macro since we do validation in RETAIL.
906 * We always validate the type here because it can come from external callers.
907 */
908void UpdateVariableHandleType(OBJECTHANDLE handle, uint32_t type)
909{
910 WRAPPER_NO_CONTRACT;
911
912 // verify that we are being asked to set a valid type
913 if (!IS_VALID_VHT_VALUE(type))
914 {
915 // bogus value passed in
916 _ASSERTE(FALSE);
917 return;
918 }
919
920 // <REVISIT_TODO> (francish) CONCURRENT GC NOTE</REVISIT_TODO>
921 //
922 // If/when concurrent GC is implemented, we need to make sure variable handles
923 // DON'T change type during an asynchronous scan, OR that we properly recover
924 // from the change. Some changes are benign, but for example changing to or
925 // from a pinning handle in the middle of a scan would not be fun.
926 //
927
928 // store the type in the handle's extra info
929 HndSetHandleExtraInfo(handle, HNDTYPE_VARIABLE, (uintptr_t)type);
930}
931
932/*
933* CompareExchangeVariableHandleType.
934*
935* Changes the dynamic type of a variable-strength handle. Unlike UpdateVariableHandleType we assume that the
936* types have already been validated.
937*/
938uint32_t CompareExchangeVariableHandleType(OBJECTHANDLE handle, uint32_t oldType, uint32_t newType)
939{
940 WRAPPER_NO_CONTRACT;
941
942 // verify that we are being asked to get/set valid types
943 _ASSERTE(IS_VALID_VHT_VALUE(oldType) && IS_VALID_VHT_VALUE(newType));
944
945 // attempt to store the type in the handle's extra info
946 return (uint32_t)HndCompareExchangeHandleExtraInfo(handle, HNDTYPE_VARIABLE, (uintptr_t)oldType, (uintptr_t)newType);
947}
948
949
950/*
951 * TraceVariableHandles.
952 *
953 * Convenience function for tracing variable-strength handles.
954 * Wraps HndScanHandlesForGC.
955 */
956void TraceVariableHandles(HANDLESCANPROC pfnTrace, uintptr_t lp1, uintptr_t lp2, uint32_t uEnableMask, uint32_t condemned, uint32_t maxgen, uint32_t flags)
957{
958 WRAPPER_NO_CONTRACT;
959
960 // set up to scan variable handles with the specified mask and trace function
961 uint32_t type = HNDTYPE_VARIABLE;
962 struct VARSCANINFO info = { (uintptr_t)uEnableMask, pfnTrace, lp2 };
963
964 HandleTableMap *walk = &g_HandleTableMap;
965 while (walk) {
966 for (uint32_t i = 0; i < INITIAL_HANDLE_TABLE_ARRAY_SIZE; i++)
967 if (walk->pBuckets[i] != NULL)
968 {
969 HHANDLETABLE hTable = walk->pBuckets[i]->pTable[getSlotNumber((ScanContext*) lp1)];
970 if (hTable)
971 {
972#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
973 if (g_fEnableARM)
974 {
975 ScanContext* sc = (ScanContext *)lp1;
976 sc->pCurrentDomain = SystemDomain::GetAppDomainAtIndex(HndGetHandleTableADIndex(hTable));
977 }
978#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
979 HndScanHandlesForGC(hTable, VariableTraceDispatcher,
980 lp1, (uintptr_t)&info, &type, 1, condemned, maxgen, HNDGCF_EXTRAINFO | flags);
981 }
982 }
983 walk = walk->pNext;
984 }
985}
986
987/*
988 loop scan version of TraceVariableHandles for single-thread-managed Ref_* functions
989 should be kept in sync with the code above
990*/
991void TraceVariableHandlesBySingleThread(HANDLESCANPROC pfnTrace, uintptr_t lp1, uintptr_t lp2, uint32_t uEnableMask, uint32_t condemned, uint32_t maxgen, uint32_t flags)
992{
993 WRAPPER_NO_CONTRACT;
994
995 // set up to scan variable handles with the specified mask and trace function
996 uint32_t type = HNDTYPE_VARIABLE;
997 struct VARSCANINFO info = { (uintptr_t)uEnableMask, pfnTrace, lp2 };
998
999 HandleTableMap *walk = &g_HandleTableMap;
1000 while (walk) {
1001 for (uint32_t i = 0; i < INITIAL_HANDLE_TABLE_ARRAY_SIZE; i ++)
1002 if (walk->pBuckets[i] != NULL)
1003 {
1004 // this is the one of Ref_* function performed by single thread in MULTI_HEAPS case, so we need to loop through all HT of the bucket
1005 for (int uCPUindex=0; uCPUindex < getNumberOfSlots(); uCPUindex++)
1006 {
1007 HHANDLETABLE hTable = walk->pBuckets[i]->pTable[uCPUindex];
1008 if (hTable)
1009 HndScanHandlesForGC(hTable, VariableTraceDispatcher,
1010 lp1, (uintptr_t)&info, &type, 1, condemned, maxgen, HNDGCF_EXTRAINFO | flags);
1011 }
1012 }
1013 walk = walk->pNext;
1014 }
1015}
1016
1017//----------------------------------------------------------------------------
1018
1019void Ref_TracePinningRoots(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Ref_promote_func* fn)
1020{
1021 WRAPPER_NO_CONTRACT;
1022
1023 LOG((LF_GC, LL_INFO10000, "Pinning referents of pinned handles in generation %u\n", condemned));
1024
1025 // pin objects pointed to by pinning handles
1026 uint32_t types[2] = {HNDTYPE_PINNED, HNDTYPE_ASYNCPINNED};
1027 uint32_t flags = sc->concurrent ? HNDGCF_ASYNC : HNDGCF_NORMAL;
1028
1029 HandleTableMap *walk = &g_HandleTableMap;
1030 while (walk) {
1031 for (uint32_t i = 0; i < INITIAL_HANDLE_TABLE_ARRAY_SIZE; i ++)
1032 if (walk->pBuckets[i] != NULL)
1033 {
1034 HHANDLETABLE hTable = walk->pBuckets[i]->pTable[getSlotNumber((ScanContext*) sc)];
1035 if (hTable)
1036 {
1037#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
1038 if (g_fEnableARM)
1039 {
1040 sc->pCurrentDomain = SystemDomain::GetAppDomainAtIndex(HndGetHandleTableADIndex(hTable));
1041 }
1042#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
1043
1044 // Pinned handles and async pinned handles are scanned in separate passes, since async pinned
1045 // handles may require a callback into the EE in order to fully trace an async pinned
1046 // object's object graph.
1047 HndScanHandlesForGC(hTable, PinObject, uintptr_t(sc), uintptr_t(fn), &types[0], 1, condemned, maxgen, flags);
1048 HndScanHandlesForGC(hTable, AsyncPinObject, uintptr_t(sc), uintptr_t(fn), &types[1], 1, condemned, maxgen, flags);
1049 }
1050 }
1051 walk = walk->pNext;
1052 }
1053
1054 // pin objects pointed to by variable handles whose dynamic type is VHT_PINNED
1055 TraceVariableHandles(PinObject, uintptr_t(sc), uintptr_t(fn), VHT_PINNED, condemned, maxgen, flags);
1056}
1057
1058
1059void Ref_TraceNormalRoots(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Ref_promote_func* fn)
1060{
1061 WRAPPER_NO_CONTRACT;
1062
1063 LOG((LF_GC, LL_INFO10000, "Promoting referents of strong handles in generation %u\n", condemned));
1064
1065 // promote objects pointed to by strong handles
1066 // during ephemeral GCs we also want to promote the ones pointed to by sizedref handles.
1067 uint32_t types[2] = {HNDTYPE_STRONG, HNDTYPE_SIZEDREF};
1068 uint32_t uTypeCount = (((condemned >= maxgen) && !g_theGCHeap->IsConcurrentGCInProgress()) ? 1 : _countof(types));
1069 uint32_t flags = (sc->concurrent) ? HNDGCF_ASYNC : HNDGCF_NORMAL;
1070
1071 HandleTableMap *walk = &g_HandleTableMap;
1072 while (walk) {
1073 for (uint32_t i = 0; i < INITIAL_HANDLE_TABLE_ARRAY_SIZE; i ++)
1074 if (walk->pBuckets[i] != NULL)
1075 {
1076 HHANDLETABLE hTable = walk->pBuckets[i]->pTable[getSlotNumber(sc)];
1077 if (hTable)
1078 {
1079#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
1080 if (g_fEnableARM)
1081 {
1082 sc->pCurrentDomain = SystemDomain::GetAppDomainAtIndex(HndGetHandleTableADIndex(hTable));
1083 }
1084#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
1085
1086 HndScanHandlesForGC(hTable, PromoteObject, uintptr_t(sc), uintptr_t(fn), types, uTypeCount, condemned, maxgen, flags);
1087 }
1088 }
1089 walk = walk->pNext;
1090 }
1091
1092 // promote objects pointed to by variable handles whose dynamic type is VHT_STRONG
1093 TraceVariableHandles(PromoteObject, uintptr_t(sc), uintptr_t(fn), VHT_STRONG, condemned, maxgen, flags);
1094
1095#if defined(FEATURE_COMINTEROP) || defined(FEATURE_REDHAWK)
1096 // don't scan ref-counted handles during concurrent phase as the clean-up of CCWs can race with AD unload and cause AV's
1097 if (!sc->concurrent)
1098 {
1099 // promote ref-counted handles
1100 uint32_t type = HNDTYPE_REFCOUNTED;
1101
1102 walk = &g_HandleTableMap;
1103 while (walk) {
1104 for (uint32_t i = 0; i < INITIAL_HANDLE_TABLE_ARRAY_SIZE; i ++)
1105 if (walk->pBuckets[i] != NULL)
1106 {
1107 HHANDLETABLE hTable = walk->pBuckets[i]->pTable[getSlotNumber(sc)];
1108 if (hTable)
1109 HndScanHandlesForGC(hTable, PromoteRefCounted, uintptr_t(sc), uintptr_t(fn), &type, 1, condemned, maxgen, flags );
1110 }
1111 walk = walk->pNext;
1112 }
1113 }
1114#endif // FEATURE_COMINTEROP || FEATURE_REDHAWK
1115}
1116
1117
1118void Ref_TraceRefCountHandles(HANDLESCANPROC callback, uintptr_t lParam1, uintptr_t lParam2)
1119{
1120#ifdef FEATURE_COMINTEROP
1121 int max_slots = getNumberOfSlots();
1122 uint32_t handleType = HNDTYPE_REFCOUNTED;
1123
1124 HandleTableMap *walk = &g_HandleTableMap;
1125 while (walk)
1126 {
1127 for (uint32_t i = 0; i < INITIAL_HANDLE_TABLE_ARRAY_SIZE; i++)
1128 {
1129 if (walk->pBuckets[i] != NULL)
1130 {
1131 for (int j = 0; j < max_slots; j++)
1132 {
1133 HHANDLETABLE hTable = walk->pBuckets[i]->pTable[j];
1134 if (hTable)
1135 HndEnumHandles(hTable, &handleType, 1, callback, lParam1, lParam2, false);
1136 }
1137 }
1138 }
1139 walk = walk->pNext;
1140 }
1141#else
1142 UNREFERENCED_PARAMETER(callback);
1143 UNREFERENCED_PARAMETER(lParam1);
1144 UNREFERENCED_PARAMETER(lParam2);
1145#endif // FEATURE_COMINTEROP
1146}
1147
1148
1149
1150
1151void Ref_CheckReachable(uint32_t condemned, uint32_t maxgen, uintptr_t lp1)
1152{
1153 WRAPPER_NO_CONTRACT;
1154
1155 LOG((LF_GC, LL_INFO10000, "Checking reachability of referents of long-weak handles in generation %u\n", condemned));
1156
1157 // these are the handle types that need to be checked
1158 uint32_t types[] =
1159 {
1160 HNDTYPE_WEAK_LONG,
1161#if defined(FEATURE_COMINTEROP) || defined(FEATURE_REDHAWK)
1162 HNDTYPE_REFCOUNTED,
1163#endif // FEATURE_COMINTEROP || FEATURE_REDHAWK
1164 };
1165
1166 // check objects pointed to by short weak handles
1167 uint32_t flags = (((ScanContext*) lp1)->concurrent) ? HNDGCF_ASYNC : HNDGCF_NORMAL;
1168 int uCPUindex = getSlotNumber((ScanContext*) lp1);
1169
1170 HandleTableMap *walk = &g_HandleTableMap;
1171 while (walk) {
1172 for (uint32_t i = 0; i < INITIAL_HANDLE_TABLE_ARRAY_SIZE; i ++)
1173 {
1174 if (walk->pBuckets[i] != NULL)
1175 {
1176 HHANDLETABLE hTable = walk->pBuckets[i]->pTable[uCPUindex];
1177 if (hTable)
1178 HndScanHandlesForGC(hTable, CheckPromoted, lp1, 0, types, _countof(types), condemned, maxgen, flags);
1179 }
1180 }
1181 walk = walk->pNext;
1182 }
1183
1184 // check objects pointed to by variable handles whose dynamic type is VHT_WEAK_LONG
1185 TraceVariableHandles(CheckPromoted, lp1, 0, VHT_WEAK_LONG, condemned, maxgen, flags);
1186}
1187
1188//
1189// Dependent handles manages the relationship between primary and secondary objects, where the lifetime of
1190// the secondary object is dependent upon that of the primary. The handle itself holds the primary instance,
1191// while the extra handle info holds the secondary object. The secondary object should always be promoted
1192// when the primary is, and the handle should be cleared if the primary is not promoted. Can't use ordinary
1193// strong handle to refer to the secondary as this could case a cycle in the graph if the secondary somehow
1194// pointed back to the primary. Can't use weak handle because that would not keep the secondary object alive.
1195//
1196// The result is that a dependenHandle has the EFFECT of
1197// * long weak handles in both the primary and secondary objects
1198// * a strong reference from the primary object to the secondary one
1199//
1200// Dependent handles are currently used for
1201//
1202// * managing fields added to EnC classes, where the handle itself holds the this pointer and the
1203// secondary object represents the new field that was added.
1204// * it is exposed to managed code (as System.Runtime.CompilerServices.DependentHandle) and is used in the
1205// implementation of ConditionWeakTable.
1206//
1207
1208// Retrieve the dependent handle context associated with the current GC scan context.
1209DhContext *Ref_GetDependentHandleContext(ScanContext* sc)
1210{
1211 WRAPPER_NO_CONTRACT;
1212 return &g_pDependentHandleContexts[getSlotNumber(sc)];
1213}
1214
1215// Scan the dependent handle table promoting any secondary object whose associated primary object is promoted.
1216//
1217// Multiple scans may be required since (a) secondary promotions made during one scan could cause the primary
1218// of another handle to be promoted and (b) the GC may not have marked all promoted objects at the time it
1219// initially calls us.
1220//
1221// Returns true if any promotions resulted from this scan.
1222bool Ref_ScanDependentHandlesForPromotion(DhContext *pDhContext)
1223{
1224 LOG((LF_GC, LL_INFO10000, "Checking liveness of referents of dependent handles in generation %u\n", pDhContext->m_iCondemned));
1225 uint32_t type = HNDTYPE_DEPENDENT;
1226 uint32_t flags = (pDhContext->m_pScanContext->concurrent) ? HNDGCF_ASYNC : HNDGCF_NORMAL;
1227 flags |= HNDGCF_EXTRAINFO;
1228
1229 // Keep a note of whether we promoted anything over the entire scan (not just the last iteration). We need
1230 // to return this data since under server GC promotions from this table may cause further promotions in
1231 // tables handled by other threads.
1232 bool fAnyPromotions = false;
1233
1234 // Keep rescanning the table while both the following conditions are true:
1235 // 1) There's at least primary object left that could have been promoted.
1236 // 2) We performed at least one secondary promotion (which could have caused a primary promotion) on the
1237 // last scan.
1238 // Note that even once we terminate the GC may call us again (because it has caused more objects to be
1239 // marked as promoted). But we scan in a loop here anyway because it is cheaper for us to loop than the GC
1240 // (especially on server GC where each external cycle has to be synchronized between GC worker threads).
1241 do
1242 {
1243 // Assume the conditions for re-scanning are both false initially. The scan callback below
1244 // (PromoteDependentHandle) will set the relevant flag on the first unpromoted primary it sees or
1245 // secondary promotion it performs.
1246 pDhContext->m_fUnpromotedPrimaries = false;
1247 pDhContext->m_fPromoted = false;
1248
1249 HandleTableMap *walk = &g_HandleTableMap;
1250 while (walk)
1251 {
1252 for (uint32_t i = 0; i < INITIAL_HANDLE_TABLE_ARRAY_SIZE; i ++)
1253 {
1254 if (walk->pBuckets[i] != NULL)
1255 {
1256 HHANDLETABLE hTable = walk->pBuckets[i]->pTable[getSlotNumber(pDhContext->m_pScanContext)];
1257 if (hTable)
1258 {
1259 HndScanHandlesForGC(hTable,
1260 PromoteDependentHandle,
1261 uintptr_t(pDhContext->m_pScanContext),
1262 uintptr_t(pDhContext->m_pfnPromoteFunction),
1263 &type, 1,
1264 pDhContext->m_iCondemned,
1265 pDhContext->m_iMaxGen,
1266 flags );
1267 }
1268 }
1269 }
1270 walk = walk->pNext;
1271 }
1272
1273 if (pDhContext->m_fPromoted)
1274 fAnyPromotions = true;
1275
1276 } while (pDhContext->m_fUnpromotedPrimaries && pDhContext->m_fPromoted);
1277
1278 return fAnyPromotions;
1279}
1280
1281// Perform a scan of dependent handles for the purpose of clearing any that haven't had their primary
1282// promoted.
1283void Ref_ScanDependentHandlesForClearing(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Ref_promote_func* fn)
1284{
1285 LOG((LF_GC, LL_INFO10000, "Clearing dead dependent handles in generation %u\n", condemned));
1286 uint32_t type = HNDTYPE_DEPENDENT;
1287 uint32_t flags = (sc->concurrent) ? HNDGCF_ASYNC : HNDGCF_NORMAL;
1288 flags |= HNDGCF_EXTRAINFO;
1289
1290 HandleTableMap *walk = &g_HandleTableMap;
1291 while (walk)
1292 {
1293 for (uint32_t i = 0; i < INITIAL_HANDLE_TABLE_ARRAY_SIZE; i ++)
1294 {
1295 if (walk->pBuckets[i] != NULL)
1296 {
1297 HHANDLETABLE hTable = walk->pBuckets[i]->pTable[getSlotNumber(sc)];
1298 if (hTable)
1299 {
1300 HndScanHandlesForGC(hTable, ClearDependentHandle, uintptr_t(sc), uintptr_t(fn), &type, 1, condemned, maxgen, flags );
1301 }
1302 }
1303 }
1304 walk = walk->pNext;
1305 }
1306}
1307
1308// Perform a scan of dependent handles for the purpose of updating handles to track relocated objects.
1309void Ref_ScanDependentHandlesForRelocation(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Ref_promote_func* fn)
1310{
1311 LOG((LF_GC, LL_INFO10000, "Relocating moved dependent handles in generation %u\n", condemned));
1312 uint32_t type = HNDTYPE_DEPENDENT;
1313 uint32_t flags = (sc->concurrent) ? HNDGCF_ASYNC : HNDGCF_NORMAL;
1314 flags |= HNDGCF_EXTRAINFO;
1315
1316 HandleTableMap *walk = &g_HandleTableMap;
1317 while (walk)
1318 {
1319 for (uint32_t i = 0; i < INITIAL_HANDLE_TABLE_ARRAY_SIZE; i ++)
1320 {
1321 if (walk->pBuckets[i] != NULL)
1322 {
1323 HHANDLETABLE hTable = walk->pBuckets[i]->pTable[getSlotNumber(sc)];
1324 if (hTable)
1325 {
1326 HndScanHandlesForGC(hTable, UpdateDependentHandle, uintptr_t(sc), uintptr_t(fn), &type, 1, condemned, maxgen, flags );
1327 }
1328 }
1329 }
1330 walk = walk->pNext;
1331 }
1332}
1333
1334/*
1335 loop scan version of TraceVariableHandles for single-thread-managed Ref_* functions
1336 should be kept in sync with the code above
1337 Only used by profiling/ETW.
1338*/
1339void TraceDependentHandlesBySingleThread(HANDLESCANPROC pfnTrace, uintptr_t lp1, uintptr_t lp2, uint32_t condemned, uint32_t maxgen, uint32_t flags)
1340{
1341 WRAPPER_NO_CONTRACT;
1342
1343 // set up to scan variable handles with the specified mask and trace function
1344 uint32_t type = HNDTYPE_DEPENDENT;
1345 struct DIAG_DEPSCANINFO info = { pfnTrace, lp2 };
1346
1347 HandleTableMap *walk = &g_HandleTableMap;
1348 while (walk) {
1349 for (uint32_t i = 0; i < INITIAL_HANDLE_TABLE_ARRAY_SIZE; i ++)
1350 if (walk->pBuckets[i] != NULL)
1351 {
1352 // this is the one of Ref_* function performed by single thread in MULTI_HEAPS case, so we need to loop through all HT of the bucket
1353 for (int uCPUindex=0; uCPUindex < getNumberOfSlots(); uCPUindex++)
1354 {
1355 HHANDLETABLE hTable = walk->pBuckets[i]->pTable[uCPUindex];
1356 if (hTable)
1357 HndScanHandlesForGC(hTable, TraceDependentHandle,
1358 lp1, (uintptr_t)&info, &type, 1, condemned, maxgen, HNDGCF_EXTRAINFO | flags);
1359 }
1360 }
1361 walk = walk->pNext;
1362 }
1363}
1364
1365// We scan handle tables by their buckets (ie, AD index). We could get into the situation where
1366// the AD indices are not very compacted (for example if we have just unloaded ADs and their
1367// indices haven't been reused yet) and we could be scanning them in an unbalanced fashion.
1368// Consider using an array to represent the compacted form of all AD indices exist for the
1369// sized ref handles.
1370void ScanSizedRefByAD(uint32_t maxgen, HANDLESCANPROC scanProc, ScanContext* sc, Ref_promote_func* fn, uint32_t flags)
1371{
1372 HandleTableMap *walk = &g_HandleTableMap;
1373 uint32_t type = HNDTYPE_SIZEDREF;
1374 int uCPUindex = getSlotNumber(sc);
1375 int n_slots = g_theGCHeap->GetNumberOfHeaps();
1376
1377 while (walk)
1378 {
1379 for (uint32_t i = 0; i < INITIAL_HANDLE_TABLE_ARRAY_SIZE; i ++)
1380 {
1381 if (walk->pBuckets[i] != NULL)
1382 {
1383 ADIndex adIndex = HndGetHandleTableADIndex(walk->pBuckets[i]->pTable[0]);
1384 if ((adIndex.m_dwIndex % n_slots) == (uint32_t)uCPUindex)
1385 {
1386 for (int index = 0; index < n_slots; index++)
1387 {
1388 HHANDLETABLE hTable = walk->pBuckets[i]->pTable[index];
1389 if (hTable)
1390 {
1391#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
1392 if (g_fEnableARM)
1393 {
1394 sc->pCurrentDomain = SystemDomain::GetAppDomainAtIndex(adIndex);
1395 }
1396#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
1397 HndScanHandlesForGC(hTable, scanProc, uintptr_t(sc), uintptr_t(fn), &type, 1, maxgen, maxgen, flags);
1398 }
1399 }
1400 }
1401 }
1402 }
1403 walk = walk->pNext;
1404 }
1405}
1406
1407void ScanSizedRefByCPU(uint32_t maxgen, HANDLESCANPROC scanProc, ScanContext* sc, Ref_promote_func* fn, uint32_t flags)
1408{
1409 HandleTableMap *walk = &g_HandleTableMap;
1410 uint32_t type = HNDTYPE_SIZEDREF;
1411 int uCPUindex = getSlotNumber(sc);
1412
1413 while (walk)
1414 {
1415 for (uint32_t i = 0; i < INITIAL_HANDLE_TABLE_ARRAY_SIZE; i ++)
1416 {
1417 if (walk->pBuckets[i] != NULL)
1418 {
1419 HHANDLETABLE hTable = walk->pBuckets[i]->pTable[uCPUindex];
1420 if (hTable)
1421 {
1422#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
1423 if (g_fEnableARM)
1424 {
1425 sc->pCurrentDomain = SystemDomain::GetAppDomainAtIndex(HndGetHandleTableADIndex(hTable));
1426 }
1427#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
1428
1429 HndScanHandlesForGC(hTable, scanProc, uintptr_t(sc), uintptr_t(fn), &type, 1, maxgen, maxgen, flags);
1430 }
1431 }
1432 }
1433 walk = walk->pNext;
1434 }
1435}
1436
1437void Ref_ScanSizedRefHandles(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Ref_promote_func* fn)
1438{
1439 LOG((LF_GC, LL_INFO10000, "Scanning SizedRef handles to in generation %u\n", condemned));
1440 UNREFERENCED_PARAMETER(condemned);
1441 _ASSERTE (condemned == maxgen);
1442 uint32_t flags = (sc->concurrent ? HNDGCF_ASYNC : HNDGCF_NORMAL) | HNDGCF_EXTRAINFO;
1443
1444 ScanSizedRefByCPU(maxgen, CalculateSizedRefSize, sc, fn, flags);
1445}
1446
1447void Ref_CheckAlive(uint32_t condemned, uint32_t maxgen, uintptr_t lp1)
1448{
1449 WRAPPER_NO_CONTRACT;
1450
1451 LOG((LF_GC, LL_INFO10000, "Checking liveness of referents of short-weak handles in generation %u\n", condemned));
1452
1453 // perform a multi-type scan that checks for unreachable objects
1454 uint32_t types[] =
1455 {
1456 HNDTYPE_WEAK_SHORT
1457#ifdef FEATURE_COMINTEROP
1458 , HNDTYPE_WEAK_WINRT
1459#endif // FEATURE_COMINTEROP
1460 };
1461 uint32_t flags = (((ScanContext*) lp1)->concurrent) ? HNDGCF_ASYNC : HNDGCF_NORMAL;
1462
1463 int uCPUindex = getSlotNumber((ScanContext*) lp1);
1464 HandleTableMap *walk = &g_HandleTableMap;
1465 while (walk)
1466 {
1467 for (uint32_t i = 0; i < INITIAL_HANDLE_TABLE_ARRAY_SIZE; i ++)
1468 {
1469 if (walk->pBuckets[i] != NULL)
1470 {
1471 HHANDLETABLE hTable = walk->pBuckets[i]->pTable[uCPUindex];
1472 if (hTable)
1473 HndScanHandlesForGC(hTable, CheckPromoted, lp1, 0, types, _countof(types), condemned, maxgen, flags);
1474 }
1475 }
1476 walk = walk->pNext;
1477 }
1478 // check objects pointed to by variable handles whose dynamic type is VHT_WEAK_SHORT
1479 TraceVariableHandles(CheckPromoted, lp1, 0, VHT_WEAK_SHORT, condemned, maxgen, flags);
1480}
1481
1482static VOLATILE(int32_t) uCount = 0;
1483
1484// NOTE: Please: if you update this function, update the very similar profiling function immediately below!!!
1485void Ref_UpdatePointers(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Ref_promote_func* fn)
1486{
1487 WRAPPER_NO_CONTRACT;
1488
1489 // For now, treat the syncblock as if it were short weak handles. <REVISIT_TODO>Later, get
1490 // the benefits of fast allocation / free & generational awareness by supporting
1491 // the SyncTable as a new block type.
1492 // @TODO cwb: wait for compelling performance measurements.</REVISIT_TODO>
1493 BOOL bDo = TRUE;
1494
1495 if (IsServerHeap())
1496 {
1497 bDo = (Interlocked::Increment(&uCount) == 1);
1498 Interlocked::CompareExchange (&uCount, 0, g_theGCHeap->GetNumberOfHeaps());
1499 _ASSERTE (uCount <= g_theGCHeap->GetNumberOfHeaps());
1500 }
1501
1502 if (bDo)
1503 GCToEEInterface::SyncBlockCacheWeakPtrScan(&UpdatePointer, uintptr_t(sc), uintptr_t(fn));
1504
1505 LOG((LF_GC, LL_INFO10000, "Updating pointers to referents of non-pinning handles in generation %u\n", condemned));
1506
1507 // these are the handle types that need their pointers updated
1508 uint32_t types[] =
1509 {
1510 HNDTYPE_WEAK_SHORT,
1511 HNDTYPE_WEAK_LONG,
1512 HNDTYPE_STRONG,
1513#if defined(FEATURE_COMINTEROP) || defined(FEATURE_REDHAWK)
1514 HNDTYPE_REFCOUNTED,
1515#endif // FEATURE_COMINTEROP || FEATURE_REDHAWK
1516#ifdef FEATURE_COMINTEROP
1517 HNDTYPE_WEAK_WINRT,
1518#endif // FEATURE_COMINTEROP
1519 HNDTYPE_SIZEDREF,
1520 };
1521
1522 // perform a multi-type scan that updates pointers
1523 uint32_t flags = (sc->concurrent) ? HNDGCF_ASYNC : HNDGCF_NORMAL;
1524
1525 HandleTableMap *walk = &g_HandleTableMap;
1526 while (walk) {
1527 for (uint32_t i = 0; i < INITIAL_HANDLE_TABLE_ARRAY_SIZE; i ++)
1528 if (walk->pBuckets[i] != NULL)
1529 {
1530 HHANDLETABLE hTable = walk->pBuckets[i]->pTable[getSlotNumber(sc)];
1531 if (hTable)
1532 HndScanHandlesForGC(hTable, UpdatePointer, uintptr_t(sc), uintptr_t(fn), types, _countof(types), condemned, maxgen, flags);
1533 }
1534 walk = walk->pNext;
1535 }
1536
1537 // update pointers in variable handles whose dynamic type is VHT_WEAK_SHORT, VHT_WEAK_LONG or VHT_STRONG
1538 TraceVariableHandles(UpdatePointer, uintptr_t(sc), uintptr_t(fn), VHT_WEAK_SHORT | VHT_WEAK_LONG | VHT_STRONG, condemned, maxgen, flags);
1539}
1540
1541#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
1542
1543// Please update this if you change the Ref_UpdatePointers function above.
1544void Ref_ScanHandlesForProfilerAndETW(uint32_t maxgen, uintptr_t lp1, handle_scan_fn fn)
1545{
1546 WRAPPER_NO_CONTRACT;
1547
1548 LOG((LF_GC | LF_CORPROF, LL_INFO10000, "Scanning all handle roots for profiler.\n"));
1549
1550 // Don't scan the sync block because they should not be reported. They are weak handles only
1551
1552 // <REVISIT_TODO>We should change the following to not report weak either
1553 // these are the handle types that need their pointers updated</REVISIT_TODO>
1554 uint32_t types[] =
1555 {
1556 HNDTYPE_WEAK_SHORT,
1557 HNDTYPE_WEAK_LONG,
1558 HNDTYPE_STRONG,
1559#if defined(FEATURE_COMINTEROP) || defined(FEATURE_REDHAWK)
1560 HNDTYPE_REFCOUNTED,
1561#endif // FEATURE_COMINTEROP || FEATURE_REDHAWK
1562#ifdef FEATURE_COMINTEROP
1563 HNDTYPE_WEAK_WINRT,
1564#endif // FEATURE_COMINTEROP
1565 HNDTYPE_PINNED,
1566// HNDTYPE_VARIABLE,
1567 HNDTYPE_ASYNCPINNED,
1568 HNDTYPE_SIZEDREF,
1569 };
1570
1571 uint32_t flags = HNDGCF_NORMAL;
1572
1573 // perform a multi-type scan that updates pointers
1574 HandleTableMap *walk = &g_HandleTableMap;
1575 while (walk) {
1576 for (uint32_t i = 0; i < INITIAL_HANDLE_TABLE_ARRAY_SIZE; i ++)
1577 if (walk->pBuckets[i] != NULL)
1578 // this is the one of Ref_* function performed by single thread in MULTI_HEAPS case, so we need to loop through all HT of the bucket
1579 for (int uCPUindex=0; uCPUindex < getNumberOfSlots(); uCPUindex++)
1580 {
1581 HHANDLETABLE hTable = walk->pBuckets[i]->pTable[uCPUindex];
1582 if (hTable)
1583 HndScanHandlesForGC(hTable, &ScanPointerForProfilerAndETW, lp1, (uintptr_t)fn, types, _countof(types), maxgen, maxgen, flags);
1584 }
1585 walk = walk->pNext;
1586 }
1587
1588 // update pointers in variable handles whose dynamic type is VHT_WEAK_SHORT, VHT_WEAK_LONG or VHT_STRONG
1589 TraceVariableHandlesBySingleThread(&ScanPointerForProfilerAndETW, lp1, (uintptr_t)fn, VHT_WEAK_SHORT | VHT_WEAK_LONG | VHT_STRONG, maxgen, maxgen, flags);
1590}
1591
1592void Ref_ScanDependentHandlesForProfilerAndETW(uint32_t maxgen, ScanContext * SC, handle_scan_fn fn)
1593{
1594 WRAPPER_NO_CONTRACT;
1595
1596 LOG((LF_GC | LF_CORPROF, LL_INFO10000, "Scanning dependent handles for profiler.\n"));
1597
1598 uint32_t flags = HNDGCF_NORMAL;
1599
1600 uintptr_t lp1 = (uintptr_t)SC;
1601 TraceDependentHandlesBySingleThread(&ScanPointerForProfilerAndETW, lp1, (uintptr_t)fn, maxgen, maxgen, flags);
1602}
1603
1604#endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
1605
1606// Callback to enumerate all object references held in handles.
1607void CALLBACK ScanPointer(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pExtraInfo, uintptr_t lp1, uintptr_t lp2)
1608{
1609 WRAPPER_NO_CONTRACT;
1610 UNREFERENCED_PARAMETER(pExtraInfo);
1611
1612 Object **pRef = (Object **)pObjRef;
1613 _ASSERTE(lp2);
1614 promote_func* callback = (promote_func*)lp2;
1615 callback(pRef, (ScanContext *)lp1, 0);
1616}
1617
1618// Enumerate all object references held by any of the handle tables in the system.
1619void Ref_ScanPointers(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Ref_promote_func* fn)
1620{
1621 WRAPPER_NO_CONTRACT;
1622
1623 uint32_t types[] =
1624 {
1625 HNDTYPE_WEAK_SHORT,
1626 HNDTYPE_WEAK_LONG,
1627 HNDTYPE_STRONG,
1628#if defined(FEATURE_COMINTEROP) || defined(FEATURE_REDHAWK)
1629 HNDTYPE_REFCOUNTED,
1630#endif // FEATURE_COMINTEROP || FEATURE_REDHAWK
1631 HNDTYPE_PINNED,
1632 HNDTYPE_ASYNCPINNED,
1633 HNDTYPE_SIZEDREF,
1634 };
1635
1636 uint32_t flags = HNDGCF_NORMAL;
1637
1638 // perform a multi-type scan that enumerates pointers
1639 for (HandleTableMap * walk = &g_HandleTableMap;
1640 walk != nullptr;
1641 walk = walk->pNext)
1642 {
1643 for (uint32_t i = 0; i < INITIAL_HANDLE_TABLE_ARRAY_SIZE; i++)
1644 {
1645 if (walk->pBuckets[i] != NULL)
1646 {
1647 // this is the one of Ref_* function performed by single thread in MULTI_HEAPS case, so we need to loop through all HT of the bucket
1648 for (int uCPUindex = 0; uCPUindex < getNumberOfSlots(); uCPUindex++)
1649 {
1650 HHANDLETABLE hTable = walk->pBuckets[i]->pTable[uCPUindex];
1651 if (hTable)
1652 HndScanHandlesForGC(hTable, &ScanPointer, uintptr_t(sc), uintptr_t(fn), types, _countof(types), condemned, maxgen, flags);
1653 }
1654 }
1655 }
1656 }
1657
1658 // enumerate pointers in variable handles whose dynamic type is VHT_WEAK_SHORT, VHT_WEAK_LONG or VHT_STRONG
1659 TraceVariableHandlesBySingleThread(&ScanPointer, uintptr_t(sc), uintptr_t(fn), VHT_WEAK_SHORT | VHT_WEAK_LONG | VHT_STRONG, condemned, maxgen, flags);
1660}
1661
1662void Ref_UpdatePinnedPointers(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Ref_promote_func* fn)
1663{
1664 WRAPPER_NO_CONTRACT;
1665
1666 LOG((LF_GC, LL_INFO10000, "Updating pointers to referents of pinning handles in generation %u\n", condemned));
1667
1668 // these are the handle types that need their pointers updated
1669 uint32_t types[2] = {HNDTYPE_PINNED, HNDTYPE_ASYNCPINNED};
1670 uint32_t flags = (sc->concurrent) ? HNDGCF_ASYNC : HNDGCF_NORMAL;
1671
1672 HandleTableMap *walk = &g_HandleTableMap;
1673 while (walk) {
1674 for (uint32_t i = 0; i < INITIAL_HANDLE_TABLE_ARRAY_SIZE; i ++)
1675 if (walk->pBuckets[i] != NULL)
1676 {
1677 HHANDLETABLE hTable = walk->pBuckets[i]->pTable[getSlotNumber(sc)];
1678 if (hTable)
1679 HndScanHandlesForGC(hTable, UpdatePointerPinned, uintptr_t(sc), uintptr_t(fn), types, _countof(types), condemned, maxgen, flags);
1680 }
1681 walk = walk->pNext;
1682 }
1683
1684 // update pointers in variable handles whose dynamic type is VHT_PINNED
1685 TraceVariableHandles(UpdatePointerPinned, uintptr_t(sc), uintptr_t(fn), VHT_PINNED, condemned, maxgen, flags);
1686}
1687
1688
1689void Ref_AgeHandles(uint32_t condemned, uint32_t maxgen, uintptr_t lp1)
1690{
1691 WRAPPER_NO_CONTRACT;
1692
1693 LOG((LF_GC, LL_INFO10000, "Aging handles in generation %u\n", condemned));
1694
1695 // these are the handle types that need their ages updated
1696 uint32_t types[] =
1697 {
1698 HNDTYPE_WEAK_SHORT,
1699 HNDTYPE_WEAK_LONG,
1700
1701 HNDTYPE_STRONG,
1702
1703 HNDTYPE_PINNED,
1704 HNDTYPE_VARIABLE,
1705#if defined(FEATURE_COMINTEROP) || defined(FEATURE_REDHAWK)
1706 HNDTYPE_REFCOUNTED,
1707#endif // FEATURE_COMINTEROP || FEATURE_REDHAWK
1708#ifdef FEATURE_COMINTEROP
1709 HNDTYPE_WEAK_WINRT,
1710#endif // FEATURE_COMINTEROP
1711 HNDTYPE_ASYNCPINNED,
1712 HNDTYPE_SIZEDREF,
1713 };
1714
1715 int uCPUindex = getSlotNumber((ScanContext*) lp1);
1716 // perform a multi-type scan that ages the handles
1717 HandleTableMap *walk = &g_HandleTableMap;
1718 while (walk) {
1719 for (uint32_t i = 0; i < INITIAL_HANDLE_TABLE_ARRAY_SIZE; i ++)
1720 if (walk->pBuckets[i] != NULL)
1721 {
1722 HHANDLETABLE hTable = walk->pBuckets[i]->pTable[uCPUindex];
1723 if (hTable)
1724 HndScanHandlesForGC(hTable, NULL, 0, 0, types, _countof(types), condemned, maxgen, HNDGCF_AGE);
1725 }
1726 walk = walk->pNext;
1727 }
1728}
1729
1730
1731void Ref_RejuvenateHandles(uint32_t condemned, uint32_t maxgen, uintptr_t lp1)
1732{
1733 WRAPPER_NO_CONTRACT;
1734
1735 LOG((LF_GC, LL_INFO10000, "Rejuvenating handles.\n"));
1736
1737 // these are the handle types that need their ages updated
1738 uint32_t types[] =
1739 {
1740 HNDTYPE_WEAK_SHORT,
1741 HNDTYPE_WEAK_LONG,
1742
1743
1744 HNDTYPE_STRONG,
1745
1746 HNDTYPE_PINNED,
1747 HNDTYPE_VARIABLE,
1748#if defined(FEATURE_COMINTEROP) || defined(FEATURE_REDHAWK)
1749 HNDTYPE_REFCOUNTED,
1750#endif // FEATURE_COMINTEROP || FEATURE_REDHAWK
1751#ifdef FEATURE_COMINTEROP
1752 HNDTYPE_WEAK_WINRT,
1753#endif // FEATURE_COMINTEROP
1754 HNDTYPE_ASYNCPINNED,
1755 HNDTYPE_SIZEDREF,
1756 };
1757
1758 int uCPUindex = getSlotNumber((ScanContext*) lp1);
1759 // reset the ages of these handles
1760 HandleTableMap *walk = &g_HandleTableMap;
1761 while (walk) {
1762 for (uint32_t i = 0; i < INITIAL_HANDLE_TABLE_ARRAY_SIZE; i ++)
1763 if (walk->pBuckets[i] != NULL)
1764 {
1765 HHANDLETABLE hTable = walk->pBuckets[i]->pTable[uCPUindex];
1766 if (hTable)
1767 HndResetAgeMap(hTable, types, _countof(types), condemned, maxgen, HNDGCF_NORMAL);
1768 }
1769 walk = walk->pNext;
1770 }
1771}
1772
1773void Ref_VerifyHandleTable(uint32_t condemned, uint32_t maxgen, ScanContext* sc)
1774{
1775 WRAPPER_NO_CONTRACT;
1776
1777 LOG((LF_GC, LL_INFO10000, "Verifying handles.\n"));
1778
1779 // these are the handle types that need to be verified
1780 uint32_t types[] =
1781 {
1782 HNDTYPE_WEAK_SHORT,
1783 HNDTYPE_WEAK_LONG,
1784
1785
1786 HNDTYPE_STRONG,
1787
1788 HNDTYPE_PINNED,
1789 HNDTYPE_VARIABLE,
1790#if defined(FEATURE_COMINTEROP) || defined(FEATURE_REDHAWK)
1791 HNDTYPE_REFCOUNTED,
1792#endif // FEATURE_COMINTEROP || FEATURE_REDHAWK
1793#ifdef FEATURE_COMINTEROP
1794 HNDTYPE_WEAK_WINRT,
1795#endif // FEATURE_COMINTEROP
1796 HNDTYPE_ASYNCPINNED,
1797 HNDTYPE_SIZEDREF,
1798 HNDTYPE_DEPENDENT,
1799 };
1800
1801 // verify these handles
1802 HandleTableMap *walk = &g_HandleTableMap;
1803 while (walk)
1804 {
1805 for (uint32_t i = 0; i < INITIAL_HANDLE_TABLE_ARRAY_SIZE; i ++)
1806 {
1807 if (walk->pBuckets[i] != NULL)
1808 {
1809 HHANDLETABLE hTable = walk->pBuckets[i]->pTable[getSlotNumber(sc)];
1810 if (hTable)
1811 HndVerifyTable(hTable, types, _countof(types), condemned, maxgen, HNDGCF_NORMAL);
1812 }
1813 }
1814 walk = walk->pNext;
1815 }
1816}
1817
1818int GetCurrentThreadHomeHeapNumber()
1819{
1820 WRAPPER_NO_CONTRACT;
1821
1822 assert(g_theGCHeap != nullptr);
1823 return g_theGCHeap->GetHomeHeapNumber();
1824}
1825
1826bool HandleTableBucket::Contains(OBJECTHANDLE handle)
1827{
1828 LIMITED_METHOD_CONTRACT;
1829
1830 if (NULL == handle)
1831 {
1832 return FALSE;
1833 }
1834
1835 HHANDLETABLE hTable = HndGetHandleTable(handle);
1836 for (int uCPUindex=0; uCPUindex < g_theGCHeap->GetNumberOfHeaps(); uCPUindex++)
1837 {
1838 if (hTable == this->pTable[uCPUindex])
1839 {
1840 return TRUE;
1841 }
1842 }
1843 return FALSE;
1844}
1845
1846#endif // !DACCESS_COMPILE
1847
1848GC_DAC_VISIBLE
1849OBJECTREF GetDependentHandleSecondary(OBJECTHANDLE handle)
1850{
1851 WRAPPER_NO_CONTRACT;
1852
1853 return UNCHECKED_OBJECTREF_TO_OBJECTREF((_UNCHECKED_OBJECTREF)HndGetHandleExtraInfo(handle));
1854}
1855
1856void PopulateHandleTableDacVars(GcDacVars* gcDacVars)
1857{
1858 static_assert(offsetof(HandleTableMap, pBuckets) == offsetof(dac_handle_table_map, pBuckets), "handle table map DAC layout mismatch");
1859 static_assert(offsetof(HandleTableMap, pNext) == offsetof(dac_handle_table_map, pNext), "handle table map DAC layout mismatch");
1860 static_assert(offsetof(HandleTableMap, dwMaxIndex) == offsetof(dac_handle_table_map, dwMaxIndex), "handle table map DAC layout mismatch");
1861 static_assert(offsetof(HandleTableBucket, pTable) == offsetof(dac_handle_table_bucket, pTable), "handle table bucket DAC layout mismatch");
1862 static_assert(offsetof(HandleTableBucket, HandleTableIndex) == offsetof(dac_handle_table_bucket, HandleTableIndex), "handle table bucket DAC layout mismatch");
1863 static_assert(offsetof(HandleTable, uADIndex) == offsetof(dac_handle_table, uADIndex), "handle table DAC layout mismatch");
1864
1865#ifndef DACCESS_COMPILE
1866 gcDacVars->handle_table_map = reinterpret_cast<dac_handle_table_map*>(&g_HandleTableMap);
1867#endif // DACCESS_COMPILE
1868}
1869