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#include "common.h"
7
8#include "finalizerthread.h"
9#include "threadsuspend.h"
10#include "jithost.h"
11
12#ifdef FEATURE_COMINTEROP
13#include "runtimecallablewrapper.h"
14#endif
15
16#ifdef FEATURE_PROFAPI_ATTACH_DETACH
17#include "profattach.h"
18#endif // FEATURE_PROFAPI_ATTACH_DETACH
19
20BOOL FinalizerThread::fRunFinalizersOnUnload = FALSE;
21BOOL FinalizerThread::fQuitFinalizer = FALSE;
22
23#if defined(__linux__) && defined(FEATURE_EVENT_TRACE)
24#define LINUX_HEAP_DUMP_TIME_OUT 10000
25
26extern bool s_forcedGCInProgress;
27ULONGLONG FinalizerThread::LastHeapDumpTime = 0;
28
29Volatile<BOOL> g_TriggerHeapDump = FALSE;
30#endif // __linux__
31
32CLREvent * FinalizerThread::hEventFinalizer = NULL;
33CLREvent * FinalizerThread::hEventFinalizerDone = NULL;
34CLREvent * FinalizerThread::hEventShutDownToFinalizer = NULL;
35CLREvent * FinalizerThread::hEventFinalizerToShutDown = NULL;
36
37HANDLE FinalizerThread::MHandles[kHandleCount];
38
39BOOL FinalizerThread::IsCurrentThreadFinalizer()
40{
41 LIMITED_METHOD_CONTRACT;
42
43 return GetThread() == g_pFinalizerThread;
44}
45
46void FinalizerThread::EnableFinalization()
47{
48 WRAPPER_NO_CONTRACT;
49
50 hEventFinalizer->Set();
51}
52
53BOOL FinalizerThread::HaveExtraWorkForFinalizer()
54{
55 WRAPPER_NO_CONTRACT;
56
57 return GetFinalizerThread()->HaveExtraWorkForFinalizer();
58}
59
60// This helper is here to avoid EH goo associated with DefineFullyQualifiedNameForStack being
61// invoked when logging is off.
62__declspec(noinline)
63void LogFinalization(Object* obj)
64{
65 STATIC_CONTRACT_NOTHROW;
66 STATIC_CONTRACT_GC_NOTRIGGER;
67 STATIC_CONTRACT_MODE_ANY;
68
69#ifdef FEATURE_EVENT_TRACE
70 ETW::GCLog::SendFinalizeObjectEvent(obj->GetMethodTable(), obj);
71#endif // FEATURE_EVENT_TRACE
72}
73
74
75void CallFinalizer(Object* obj)
76{
77 STATIC_CONTRACT_THROWS;
78 STATIC_CONTRACT_GC_TRIGGERS;
79 STATIC_CONTRACT_MODE_COOPERATIVE;
80
81 MethodTable *pMT = obj->GetMethodTable();
82 STRESS_LOG2(LF_GC, LL_INFO1000, "Finalizing object %p MT %pT\n", obj, pMT);
83 LOG((LF_GC, LL_INFO1000, "Finalizing " LOG_OBJECT_CLASS(obj)));
84
85 _ASSERTE(GetThread()->PreemptiveGCDisabled());
86 // if we don't have a class, we can't call the finalizer
87 // if the object has been marked run as finalizer run don't call either
88 if (pMT)
89 {
90 if (!((obj->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN))
91 {
92
93 _ASSERTE(obj->GetMethodTable() == pMT);
94 _ASSERTE(pMT->HasFinalizer());
95
96 LogFinalization(obj);
97 MethodTable::CallFinalizer(obj);
98 }
99 else
100 {
101 //reset the bit so the object can be put on the list
102 //with RegisterForFinalization
103 obj->GetHeader()->ClrBit (BIT_SBLK_FINALIZER_RUN);
104 }
105 }
106}
107
108struct FinalizeAllObjects_Args {
109 OBJECTREF fobj;
110 int bitToCheck;
111};
112
113void FinalizerThread::FinalizeAllObjects_Wrapper(void *ptr)
114{
115 STATIC_CONTRACT_THROWS;
116 STATIC_CONTRACT_GC_TRIGGERS;
117 STATIC_CONTRACT_MODE_COOPERATIVE;
118
119 FinalizeAllObjects_Args *args = (FinalizeAllObjects_Args *) ptr;
120 _ASSERTE(args->fobj);
121 Object *fobj = OBJECTREFToObject(args->fobj);
122 args->fobj = NULL; // don't want to do this guy again, if we take an exception here:
123 args->fobj = ObjectToOBJECTREF(FinalizeAllObjects(fobj, args->bitToCheck));
124}
125
126// The following is inadequate when we have multiple Finalizer threads in some future release.
127// Instead, we will have to store this in TLS or pass it through the call tree of finalization.
128// It is used to tie together the base exception handling and the AppDomain transition exception
129// handling for this thread.
130static struct ManagedThreadCallState *pThreadTurnAround;
131
132Object * FinalizerThread::DoOneFinalization(Object* fobj, Thread* pThread,int bitToCheck,bool *pbTerminate)
133{
134 STATIC_CONTRACT_THROWS;
135 STATIC_CONTRACT_GC_TRIGGERS;
136 STATIC_CONTRACT_MODE_COOPERATIVE;
137
138 bool fTerminate=false;
139 Object *pReturnObject = NULL;
140
141
142 AppDomain* targetAppDomain = fobj->GetAppDomain();
143 AppDomain* currentDomain = pThread->GetDomain();
144 if (! targetAppDomain)
145 {
146 // if can't get into domain to finalize it, then it must be agile so finalize in current domain
147 targetAppDomain = currentDomain;
148 }
149
150 if (targetAppDomain == currentDomain)
151 {
152 class ResetFinalizerStartTime
153 {
154 public:
155 ResetFinalizerStartTime()
156 {
157 if (CLRHosted())
158 {
159 g_ObjFinalizeStartTime = CLRGetTickCount64();
160 }
161 }
162 ~ResetFinalizerStartTime()
163 {
164 if (g_ObjFinalizeStartTime)
165 {
166 g_ObjFinalizeStartTime = 0;
167 }
168 }
169 };
170 {
171 ThreadLocaleHolder localeHolder;
172
173 {
174 ResetFinalizerStartTime resetTime;
175 CallFinalizer(fobj);
176 }
177 }
178 pThread->InternalReset();
179 }
180 else
181 {
182 if (!currentDomain->IsDefaultDomain())
183 {
184 // this means we are in some other domain, so need to return back out through the DoADCallback
185 // and handle the object from there in another domain.
186 pReturnObject = fobj;
187 fTerminate = true;
188 }
189 else
190 {
191 // otherwise call back to ourselves to process as many as we can in that other domain
192 FinalizeAllObjects_Args args;
193 args.fobj = ObjectToOBJECTREF(fobj);
194 args.bitToCheck = bitToCheck;
195 GCPROTECT_BEGIN(args.fobj);
196 {
197 ThreadLocaleHolder localeHolder;
198
199 _ASSERTE(pThreadTurnAround != NULL);
200 ManagedThreadBase::FinalizerAppDomain(targetAppDomain,
201 FinalizeAllObjects_Wrapper,
202 &args,
203 pThreadTurnAround);
204 }
205 pThread->InternalReset();
206 // process the object we got back or be done if we got back null
207 pReturnObject = OBJECTREFToObject(args.fobj);
208 GCPROTECT_END();
209 }
210 }
211
212 *pbTerminate = fTerminate;
213 return pReturnObject;
214}
215
216Object * FinalizerThread::FinalizeAllObjects(Object* fobj, int bitToCheck)
217{
218 STATIC_CONTRACT_THROWS;
219 STATIC_CONTRACT_GC_TRIGGERS;
220 STATIC_CONTRACT_MODE_COOPERATIVE;
221
222 FireEtwGCFinalizersBegin_V1(GetClrInstanceId());
223
224 unsigned int fcount = 0;
225 bool fTerminate = false;
226
227 if (fobj == NULL)
228 {
229 fobj = GCHeapUtilities::GetGCHeap()->GetNextFinalizable();
230 }
231
232 Thread *pThread = GetThread();
233
234#ifdef FEATURE_PROFAPI_ATTACH_DETACH
235 ULONGLONG ui64TimestampLastCheckedProfAttachEventMs = 0;
236#endif //FEATURE_PROFAPI_ATTACH_DETACH
237
238 // Finalize everyone
239 while (fobj)
240 {
241#ifdef FEATURE_PROFAPI_ATTACH_DETACH
242 // Don't let an overloaded finalizer queue starve out
243 // an attaching profiler. In between running finalizers,
244 // check the profiler attach event without blocking.
245 ProcessProfilerAttachIfNecessary(&ui64TimestampLastCheckedProfAttachEventMs);
246#endif // FEATURE_PROFAPI_ATTACH_DETACH
247
248 if (fobj->GetHeader()->GetBits() & bitToCheck)
249 {
250 fobj = GCHeapUtilities::GetGCHeap()->GetNextFinalizable();
251 }
252 else
253 {
254 fcount++;
255 fobj = DoOneFinalization(fobj, pThread, bitToCheck,&fTerminate);
256 if (fTerminate)
257 {
258 break;
259 }
260
261 if (fobj == NULL)
262 {
263 fobj = GCHeapUtilities::GetGCHeap()->GetNextFinalizable();
264 }
265 }
266 }
267 FireEtwGCFinalizersEnd_V1(fcount, GetClrInstanceId());
268
269 return fobj;
270}
271
272
273#ifdef FEATURE_PROFAPI_ATTACH_DETACH
274
275// ----------------------------------------------------------------------------
276// ProcessProfilerAttachIfNecessary
277//
278// Description:
279// This is called to peek at the Profiler Attach Event in between finalizers to check
280// if it's signaled. If it is, this calls
281// code:ProfilingAPIAttachDetach::ProcessSignaledAttachEvent to deal with it.
282//
283//
284// Arguments:
285// * pui64TimestampLastCheckedEventMs: [in / out] This keeps track of how often the
286// Profiler Attach Event is checked, so it's not checked too often during a
287// tight loop (in particular, the loop in code:SVR::FinalizeAllObjects which
288// executes all finalizer routines in the queue). This argument has the
289// following possible values:
290// * [in] (pui64TimestampLastCheckedEventMs) == NULL: Means the arg is not used, so
291// just check the event and ignore this argument
292// * [in] (*pui64TimestampLastCheckedEventMs) == 0: Arg is uninitialized. Just
293// initialize it with the current tick count and return without checking the
294// event (as the event was probably just checked before entering the loop
295// that called this function).
296// * [in] (*pui64TimestampLastCheckedEventMs) != 0: Arg is initialized to the
297// approximate tick count of when the event was last checked. If it's time
298// to check the event again, do so and update this parameter on [out] with
299// the current timestamp. Otherwise, do nothing and return.
300//
301// Notes:
302// * The Profiler Attach Event is also checked in the main WaitForMultipleObjects in
303// WaitForFinalizerEvent
304//
305
306// static
307void FinalizerThread::ProcessProfilerAttachIfNecessary(ULONGLONG * pui64TimestampLastCheckedEventMs)
308{
309 STATIC_CONTRACT_NOTHROW;
310 STATIC_CONTRACT_GC_NOTRIGGER;
311 STATIC_CONTRACT_MODE_ANY;
312
313 if (MHandles[kProfilingAPIAttach] == NULL)
314 {
315 return;
316 }
317
318 if (pui64TimestampLastCheckedEventMs != NULL)
319 {
320 if (*pui64TimestampLastCheckedEventMs == 0)
321 {
322 // Just initialize timestamp and leave
323 *pui64TimestampLastCheckedEventMs = CLRGetTickCount64();
324 return;
325 }
326
327 static DWORD dwMsBetweenCheckingProfAPIAttachEvent = 0;
328 if (dwMsBetweenCheckingProfAPIAttachEvent == 0)
329 {
330 // First time through, initialize with how long to wait between checking the
331 // event.
332 dwMsBetweenCheckingProfAPIAttachEvent = CLRConfig::GetConfigValue(
333 CLRConfig::EXTERNAL_MsBetweenAttachCheck);
334 }
335 ULONGLONG ui64TimestampNowMs = CLRGetTickCount64();
336 _ASSERTE(ui64TimestampNowMs >= (*pui64TimestampLastCheckedEventMs));
337 if (ui64TimestampNowMs - (*pui64TimestampLastCheckedEventMs) <
338 dwMsBetweenCheckingProfAPIAttachEvent)
339 {
340 // Too soon, go home
341 return;
342 }
343
344 // Otherwise, update the timestamp and wait on the finalizer event below
345 *pui64TimestampLastCheckedEventMs = ui64TimestampNowMs;
346 }
347
348 // Check the attach event without waiting; only if it's signaled right now will we
349 // process the event.
350 if (WaitForSingleObject(MHandles[kProfilingAPIAttach], 0) != WAIT_OBJECT_0)
351 {
352 // Any return value that indicates we can't verify the attach event is signaled
353 // right now means we should just forget about it and immediately return to
354 // whatever we were doing
355 return;
356 }
357
358 // Event is signaled; process it by spawning a new thread to do the work
359 ProfilingAPIAttachDetach::ProcessSignaledAttachEvent();
360}
361
362#endif // FEATURE_PROFAPI_ATTACH_DETACH
363
364void FinalizerThread::WaitForFinalizerEvent (CLREvent *event)
365{
366 // Non-host environment
367
368 // We don't want kLowMemoryNotification to starve out kFinalizer
369 // (as the latter may help correct the former), and we don't want either
370 // to starve out kProfilingAPIAttach, as we want decent responsiveness
371 // to a user trying to attach a profiler. So check in this order:
372 // kProfilingAPIAttach alone (0 wait)
373 // kFinalizer alone (2s wait)
374 // all events together (infinite wait)
375
376#ifdef FEATURE_PROFAPI_ATTACH_DETACH
377 // NULL means check attach event now, and don't worry about how long it was since
378 // the last time the event was checked.
379 ProcessProfilerAttachIfNecessary(NULL);
380#endif // FEATURE_PROFAPI_ATTACH_DETACH
381
382 //give a chance to the finalizer event (2s)
383 switch (event->Wait(2000, FALSE))
384 {
385 case (WAIT_OBJECT_0):
386 return;
387 case (WAIT_ABANDONED):
388 return;
389 case (WAIT_TIMEOUT):
390 break;
391 }
392 MHandles[kFinalizer] = event->GetHandleUNHOSTED();
393 while (1)
394 {
395 // WaitForMultipleObjects will wait on the event handles in MHandles
396 // starting at this offset
397 UINT uiEventIndexOffsetForWait = 0;
398
399 // WaitForMultipleObjects will wait on this number of event handles
400 DWORD cEventsForWait = kHandleCount;
401
402 // #MHandleTypeValues:
403 // WaitForMultipleObjects will now wait on a subset of the events in the
404 // MHandles array. At this point kFinalizer should have a non-NULL entry
405 // in the array. Wait on the following events:
406 //
407 // * kLowMemoryNotification (if it's non-NULL && g_fEEStarted)
408 // * kFinalizer (always)
409 // * kProfilingAPIAttach (if it's non-NULL)
410 //
411 // The enum code:MHandleType values become important here, as
412 // WaitForMultipleObjects needs to wait on a contiguous set of non-NULL
413 // entries in MHandles, so we'll assert the values are contiguous as we
414 // expect.
415 _ASSERTE(kLowMemoryNotification == 0);
416 _ASSERTE((kFinalizer == 1) && (MHandles[1] != NULL));
417#ifdef FEATURE_PROFAPI_ATTACH_DETACH
418 _ASSERTE(kProfilingAPIAttach == 2);
419#endif //FEATURE_PROFAPI_ATTACH_DETACH
420
421 // Exclude the low-memory notification event from the wait if the event
422 // handle is NULL or the EE isn't fully started up yet.
423 if ((MHandles[kLowMemoryNotification] == NULL) || !g_fEEStarted)
424 {
425 uiEventIndexOffsetForWait = kLowMemoryNotification + 1;
426 cEventsForWait--;
427 }
428
429#ifdef FEATURE_PROFAPI_ATTACH_DETACH
430 // Exclude kProfilingAPIAttach if it's NULL
431 if (MHandles[kProfilingAPIAttach] == NULL)
432 {
433 cEventsForWait--;
434 }
435#endif //FEATURE_PROFAPI_ATTACH_DETACH
436
437 switch (WaitForMultipleObjectsEx(
438 cEventsForWait, // # objects to wait on
439 &(MHandles[uiEventIndexOffsetForWait]), // array of objects to wait on
440 FALSE, // bWaitAll == FALSE, so wait for first signal
441#if defined(__linux__) && defined(FEATURE_EVENT_TRACE)
442 LINUX_HEAP_DUMP_TIME_OUT,
443#else
444 INFINITE, // timeout
445#endif
446 FALSE) // alertable
447
448 // Adjust the returned array index for the offset we used, so the return
449 // value is relative to entire MHandles array
450 + uiEventIndexOffsetForWait)
451 {
452 case (WAIT_OBJECT_0 + kLowMemoryNotification):
453 //short on memory GC immediately
454 GetFinalizerThread()->DisablePreemptiveGC();
455 GCHeapUtilities::GetGCHeap()->GarbageCollect(0, true);
456 GetFinalizerThread()->EnablePreemptiveGC();
457 //wait only on the event for 2s
458 switch (event->Wait(2000, FALSE))
459 {
460 case (WAIT_OBJECT_0):
461 return;
462 case (WAIT_ABANDONED):
463 return;
464 case (WAIT_TIMEOUT):
465 break;
466 }
467 break;
468 case (WAIT_OBJECT_0 + kFinalizer):
469 return;
470#ifdef FEATURE_PROFAPI_ATTACH_DETACH
471 case (WAIT_OBJECT_0 + kProfilingAPIAttach):
472 // Spawn thread to perform the profiler attach, then resume our wait
473 ProfilingAPIAttachDetach::ProcessSignaledAttachEvent();
474 break;
475#endif // FEATURE_PROFAPI_ATTACH_DETACH
476#if defined(__linux__) && defined(FEATURE_EVENT_TRACE)
477 case (WAIT_TIMEOUT + kLowMemoryNotification):
478 case (WAIT_TIMEOUT + kFinalizer):
479 if (g_TriggerHeapDump)
480 {
481 return;
482 }
483
484 break;
485#endif
486 default:
487 //what's wrong?
488 _ASSERTE (!"Bad return code from WaitForMultipleObjects");
489 return;
490 }
491 }
492}
493
494
495
496static BOOL s_FinalizerThreadOK = FALSE;
497
498
499
500VOID FinalizerThread::FinalizerThreadWorker(void *args)
501{
502 // TODO: The following line should be removed after contract violation is fixed.
503 // See bug 27409
504 SCAN_IGNORE_THROW;
505 SCAN_IGNORE_TRIGGER;
506
507 // This is used to stitch together the exception handling at the base of our thread with
508 // any eventual transitions into different AppDomains for finalization.
509 _ASSERTE(args != NULL);
510 pThreadTurnAround = (ManagedThreadCallState *) args;
511
512 BOOL bPriorityBoosted = FALSE;
513
514 while (!fQuitFinalizer)
515 {
516 // Wait for work to do...
517
518 _ASSERTE(GetFinalizerThread()->PreemptiveGCDisabled());
519#ifdef _DEBUG
520 if (g_pConfig->FastGCStressLevel())
521 {
522 GetFinalizerThread()->m_GCOnTransitionsOK = FALSE;
523 }
524#endif
525 GetFinalizerThread()->EnablePreemptiveGC();
526#ifdef _DEBUG
527 if (g_pConfig->FastGCStressLevel())
528 {
529 GetFinalizerThread()->m_GCOnTransitionsOK = TRUE;
530 }
531#endif
532#if 0
533 // Setting the event here, instead of at the bottom of the loop, could
534 // cause us to skip draining the Q, if the request is made as soon as
535 // the app starts running.
536 SignalFinalizationDone(TRUE);
537#endif //0
538
539 WaitForFinalizerEvent (hEventFinalizer);
540
541#if defined(__linux__) && defined(FEATURE_EVENT_TRACE)
542 if (g_TriggerHeapDump && (CLRGetTickCount64() > (LastHeapDumpTime + LINUX_HEAP_DUMP_TIME_OUT)))
543 {
544 s_forcedGCInProgress = true;
545 GetFinalizerThread()->DisablePreemptiveGC();
546 GCHeapUtilities::GetGCHeap()->GarbageCollect(2, false, collection_blocking);
547 GetFinalizerThread()->EnablePreemptiveGC();
548 s_forcedGCInProgress = false;
549
550 LastHeapDumpTime = CLRGetTickCount64();
551 g_TriggerHeapDump = FALSE;
552 }
553#endif
554
555 if (!bPriorityBoosted)
556 {
557 if (GetFinalizerThread()->SetThreadPriority(THREAD_PRIORITY_HIGHEST))
558 bPriorityBoosted = TRUE;
559 }
560
561 JitHost::Reclaim();
562
563 GetFinalizerThread()->DisablePreemptiveGC();
564
565#ifdef _DEBUG
566 // <TODO> workaround. make finalization very lazy for gcstress 3 or 4.
567 // only do finalization if the system is quiescent</TODO>
568 if (g_pConfig->GetGCStressLevel() > 1)
569 {
570 size_t last_gc_count;
571 DWORD dwSwitchCount = 0;
572
573 do
574 {
575 last_gc_count = GCHeapUtilities::GetGCHeap()->CollectionCount(0);
576 GetFinalizerThread()->m_GCOnTransitionsOK = FALSE;
577 GetFinalizerThread()->EnablePreemptiveGC();
578 __SwitchToThread (0, ++dwSwitchCount);
579 GetFinalizerThread()->DisablePreemptiveGC();
580 // If no GCs happended, then we assume we are quiescent
581 GetFinalizerThread()->m_GCOnTransitionsOK = TRUE;
582 } while (GCHeapUtilities::GetGCHeap()->CollectionCount(0) - last_gc_count > 0);
583 }
584#endif //_DEBUG
585
586 // we might want to do some extra work on the finalizer thread
587 // check and do it
588 if (GetFinalizerThread()->HaveExtraWorkForFinalizer())
589 {
590 GetFinalizerThread()->DoExtraWorkForFinalizer();
591 }
592 LOG((LF_GC, LL_INFO100, "***** Calling Finalizers\n"));
593 // We may mark the finalizer thread for abort. If so the abort request is for previous finalizer method, not for next one.
594 if (GetFinalizerThread()->IsAbortRequested())
595 {
596 GetFinalizerThread()->EEResetAbort(Thread::TAR_ALL);
597 }
598 FastInterlockExchange ((LONG*)&g_FinalizerIsRunning, TRUE);
599
600 FinalizeAllObjects(NULL, 0);
601 _ASSERTE(GetFinalizerThread()->GetDomain()->IsDefaultDomain());
602
603 FastInterlockExchange ((LONG*)&g_FinalizerIsRunning, FALSE);
604 // We may still have the finalizer thread for abort. If so the abort request is for previous finalizer method, not for next one.
605 if (GetFinalizerThread()->IsAbortRequested())
606 {
607 GetFinalizerThread()->EEResetAbort(Thread::TAR_ALL);
608 }
609
610 // Increment the loop count. This is currently used by the AddMemoryPressure heuristic to see
611 // if finalizers have run since the last time it triggered GC.
612 FastInterlockIncrement((LONG *)&g_FinalizerLoopCount);
613
614 // Anyone waiting to drain the Q can now wake up. Note that there is a
615 // race in that another thread starting a drain, as we leave a drain, may
616 // consider itself satisfied by the drain that just completed. This is
617 // acceptable.
618 SignalFinalizationDone(TRUE);
619 }
620}
621
622
623// During shutdown, finalize all objects that haven't been run yet... whether reachable or not.
624void FinalizerThread::FinalizeObjectsOnShutdown(LPVOID args)
625{
626 WRAPPER_NO_CONTRACT;
627
628 // This is used to stitch together the exception handling at the base of our thread with
629 // any eventual transitions into different AppDomains for finalization.
630 _ASSERTE(args != NULL);
631 pThreadTurnAround = (ManagedThreadCallState *) args;
632
633 FinalizeAllObjects(NULL, BIT_SBLK_FINALIZER_RUN);
634}
635
636
637DWORD WINAPI FinalizerThread::FinalizerThreadStart(void *args)
638{
639 ClrFlsSetThreadType (ThreadType_Finalizer);
640
641 ASSERT(args == 0);
642 ASSERT(hEventFinalizer->IsValid());
643
644 // TODO: The following line should be removed after contract violation is fixed.
645 // See bug 27409
646 SCAN_IGNORE_THROW;
647 SCAN_IGNORE_TRIGGER;
648
649 LOG((LF_GC, LL_INFO10, "Finalizer thread starting...\n"));
650
651 _ASSERTE(GetFinalizerThread()->GetDomain()->IsDefaultDomain());
652
653#if defined(FEATURE_COMINTEROP_APARTMENT_SUPPORT) && !defined(FEATURE_COMINTEROP)
654 // Make sure the finalizer thread is set to MTA to avoid hitting
655 // DevDiv Bugs 180773 - [Stress Failure] AV at CoreCLR!SafeQueryInterfaceHelper
656 GetFinalizerThread()->SetApartment(Thread::AS_InMTA, FALSE);
657#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT && !FEATURE_COMINTEROP
658
659 s_FinalizerThreadOK = GetFinalizerThread()->HasStarted();
660
661 _ASSERTE(s_FinalizerThreadOK);
662 _ASSERTE(GetThread() == GetFinalizerThread());
663
664 // finalizer should always park in default domain
665
666 if (s_FinalizerThreadOK)
667 {
668 INSTALL_UNHANDLED_MANAGED_EXCEPTION_TRAP;
669
670#ifdef _DEBUG // The only purpose of this try/finally is to trigger an assertion
671 EE_TRY_FOR_FINALLY(void *, unused, NULL)
672 {
673#endif
674 GetFinalizerThread()->SetBackground(TRUE);
675
676 EnsureYieldProcessorNormalizedInitialized();
677
678#ifdef FEATURE_PROFAPI_ATTACH_DETACH
679 // Add the Profiler Attach Event to the array of event handles that the
680 // finalizer thread waits on. If the process is not enabled for profiler
681 // attach (e.g., running memory- or sync-hosted, or there is some other error
682 // that causes the Profiler Attach Event not to be created), then this just
683 // adds NULL in the slot where the Profiler Attach Event handle would go. In
684 // this case, WaitForFinalizerEvent will know to ignore that handle when it
685 // waits.
686 //
687 // Calling ProfilingAPIAttachDetach::GetAttachEvent induces lazy
688 // initialization of the profiling API attach/detach support objects,
689 // including the event itself and its security descriptor. So switch to
690 // preemptive mode during these OS calls
691 GetFinalizerThread()->EnablePreemptiveGC();
692 MHandles[kProfilingAPIAttach] = ::ProfilingAPIAttachDetach::GetAttachEvent();
693 GetFinalizerThread()->DisablePreemptiveGC();
694#endif // FEATURE_PROFAPI_ATTACH_DETACH
695
696 while (!fQuitFinalizer)
697 {
698 // This will apply any policy for swallowing exceptions during normal
699 // processing, without allowing the finalizer thread to disappear on us.
700 ManagedThreadBase::FinalizerBase(FinalizerThreadWorker);
701
702 // If we came out on an exception, then we probably lost the signal that
703 // there are objects in the queue ready to finalize. The safest thing is
704 // to reenable finalization.
705 if (!fQuitFinalizer)
706 EnableFinalization();
707 }
708
709 // Tell shutdown thread we are done with finalizing dead objects.
710 hEventFinalizerToShutDown->Set();
711
712 // Wait for shutdown thread to signal us.
713 GetFinalizerThread()->EnablePreemptiveGC();
714 hEventShutDownToFinalizer->Wait(INFINITE,FALSE);
715 GetFinalizerThread()->DisablePreemptiveGC();
716
717 AppDomain::RaiseExitProcessEvent();
718
719 hEventFinalizerToShutDown->Set();
720
721 // Phase 1 ends.
722 // Now wait for Phase 2 signal.
723
724 // Wait for shutdown thread to signal us.
725 GetFinalizerThread()->EnablePreemptiveGC();
726 hEventShutDownToFinalizer->Wait(INFINITE,FALSE);
727 GetFinalizerThread()->DisablePreemptiveGC();
728
729 // We have been asked to quit, so must be shutting down
730 _ASSERTE(g_fEEShutDown);
731 _ASSERTE(GetFinalizerThread()->PreemptiveGCDisabled());
732
733 if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_FinalizeOnShutdown) != 0)
734 {
735 // Finalize all registered objects during shutdown, even they are still reachable.
736 GCHeapUtilities::GetGCHeap()->SetFinalizeQueueForShutdown(FALSE);
737
738 // This will apply any policy for swallowing exceptions during normal
739 // processing, without allowing the finalizer thread to disappear on us.
740 ManagedThreadBase::FinalizerBase(FinalizeObjectsOnShutdown);
741 }
742
743 _ASSERTE(GetFinalizerThread()->GetDomain()->IsDefaultDomain());
744
745 // we might want to do some extra work on the finalizer thread
746 // check and do it
747 if (GetFinalizerThread()->HaveExtraWorkForFinalizer())
748 {
749 GetFinalizerThread()->DoExtraWorkForFinalizer();
750 }
751
752 hEventFinalizerToShutDown->Set();
753
754 // Wait for shutdown thread to signal us.
755 GetFinalizerThread()->EnablePreemptiveGC();
756 hEventShutDownToFinalizer->Wait(INFINITE,FALSE);
757 GetFinalizerThread()->DisablePreemptiveGC();
758
759#ifdef FEATURE_COMINTEROP
760 // Do extra cleanup for part 1 of shutdown.
761 // If we hang here (bug 87809) shutdown thread will
762 // timeout on us and will proceed normally
763 //
764 // We cannot call CoEEShutDownCOM, since the BEGIN_EXTERNAL_ENTRYPOINT
765 // will turn our call into a NOP. We can no longer execute managed
766 // code for an external caller.
767 InnerCoEEShutDownCOM();
768#endif // FEATURE_COMINTEROP
769
770 hEventFinalizerToShutDown->Set();
771
772#ifdef _DEBUG // The only purpose of this try/finally is to trigger an assertion
773 }
774 EE_FINALLY
775 {
776 // We can have exception to reach here if policy tells us to
777 // let exception go on finalizer thread.
778 //
779 if (GOT_EXCEPTION() && SwallowUnhandledExceptions())
780 _ASSERTE(!"Exception in the finalizer thread!");
781
782 }
783 EE_END_FINALLY;
784#endif
785 UNINSTALL_UNHANDLED_MANAGED_EXCEPTION_TRAP;
786 }
787 // finalizer should always park in default domain
788 _ASSERTE(GetThread()->GetDomain()->IsDefaultDomain());
789
790 LOG((LF_GC, LL_INFO10, "Finalizer thread done."));
791
792 // Enable pre-emptive GC before we leave so that anybody trying to suspend
793 // us will not end up waiting forever. Don't do a DestroyThread because this
794 // will happen soon when we tear down the thread store.
795 GetFinalizerThread()->EnablePreemptiveGC();
796
797 // We do not want to tear Finalizer thread,
798 // since doing so will cause OLE32 to CoUninitialize.
799 while (1)
800 {
801 PAL_TRY(void *, unused, NULL)
802 {
803 __SwitchToThread(INFINITE, CALLER_LIMITS_SPINNING);
804 }
805 PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
806 {
807 }
808 PAL_ENDTRY
809 }
810
811 return 0;
812}
813
814void FinalizerThread::FinalizerThreadCreate()
815{
816 CONTRACTL{
817 THROWS;
818 GC_TRIGGERS;
819 MODE_ANY;
820 } CONTRACTL_END;
821
822#ifndef FEATURE_PAL
823 MHandles[kLowMemoryNotification] =
824 CreateMemoryResourceNotification(LowMemoryResourceNotification);
825#endif // FEATURE_PAL
826
827 hEventFinalizerDone = new CLREvent();
828 hEventFinalizerDone->CreateManualEvent(FALSE);
829 hEventFinalizer = new CLREvent();
830 hEventFinalizer->CreateAutoEvent(FALSE);
831 hEventFinalizerToShutDown = new CLREvent();
832 hEventFinalizerToShutDown->CreateAutoEvent(FALSE);
833 hEventShutDownToFinalizer = new CLREvent();
834 hEventShutDownToFinalizer->CreateAutoEvent(FALSE);
835
836 _ASSERTE(g_pFinalizerThread == 0);
837 g_pFinalizerThread = SetupUnstartedThread();
838
839 // We don't want the thread block disappearing under us -- even if the
840 // actual thread terminates.
841 GetFinalizerThread()->IncExternalCount();
842
843 if (GetFinalizerThread()->CreateNewThread(0, &FinalizerThreadStart, NULL, W(".NET Finalizer")) )
844 {
845 DWORD dwRet = GetFinalizerThread()->StartThread();
846
847 // When running under a user mode native debugger there is a race
848 // between the moment we've created the thread (in CreateNewThread) and
849 // the moment we resume it (in StartThread); the debugger may receive
850 // the "ct" (create thread) notification, and it will attempt to
851 // suspend/resume all threads in the process. Now imagine the debugger
852 // resumes this thread first, and only later does it try to resume the
853 // newly created thread (the finalizer thread). In these conditions our
854 // call to ResumeThread may come before the debugger's call to ResumeThread
855 // actually causing dwRet to equal 2.
856 // We cannot use IsDebuggerPresent() in the condition below because the
857 // debugger may have been detached between the time it got the notification
858 // and the moment we execute the test below.
859 _ASSERTE(dwRet == 1 || dwRet == 2);
860 }
861}
862
863void FinalizerThread::SignalFinalizationDone(BOOL fFinalizer)
864{
865 WRAPPER_NO_CONTRACT;
866
867 if (fFinalizer)
868 {
869 FastInterlockAnd((DWORD*)&g_FinalizerWaiterStatus, ~FWS_WaitInterrupt);
870 }
871 hEventFinalizerDone->Set();
872}
873
874// Wait for the finalizer thread to complete one pass.
875void FinalizerThread::FinalizerThreadWait(DWORD timeout)
876{
877 ASSERT(hEventFinalizerDone->IsValid());
878 ASSERT(hEventFinalizer->IsValid());
879 ASSERT(GetFinalizerThread());
880
881 // Can't call this from within a finalized method.
882 if (!IsCurrentThreadFinalizer())
883 {
884#ifdef FEATURE_COMINTEROP
885 // To help combat finalizer thread starvation, we check to see if there are any wrappers
886 // scheduled to be cleaned up for our context. If so, we'll do them here to avoid making
887 // the finalizer thread do a transition.
888 if (g_pRCWCleanupList != NULL)
889 g_pRCWCleanupList->CleanupWrappersInCurrentCtxThread();
890#endif // FEATURE_COMINTEROP
891
892 GCX_PREEMP();
893
894 Thread *pThread = GetThread();
895 BOOL fADUnloadHelper = (pThread && pThread->HasThreadStateNC(Thread::TSNC_ADUnloadHelper));
896
897 ULONGLONG startTime = CLRGetTickCount64();
898 ULONGLONG endTime;
899 if (timeout == INFINITE)
900 {
901 endTime = MAXULONGLONG;
902 }
903 else
904 {
905 endTime = timeout + startTime;
906 }
907
908 while (TRUE)
909 {
910 hEventFinalizerDone->Reset();
911 EnableFinalization();
912
913 //----------------------------------------------------
914 // Do appropriate wait and pump messages if necessary
915 //----------------------------------------------------
916 //WaitForSingleObject(hEventFinalizerDone, INFINITE);
917
918 if (fADUnloadHelper)
919 {
920 timeout = GetEEPolicy()->GetTimeout(OPR_FinalizerRun);
921 }
922
923 DWORD status = hEventFinalizerDone->Wait(timeout,TRUE);
924 if (status != WAIT_TIMEOUT && !(g_FinalizerWaiterStatus & FWS_WaitInterrupt))
925 {
926 return;
927 }
928 if (!fADUnloadHelper)
929 {
930 // recalculate timeout
931 if (timeout != INFINITE)
932 {
933 ULONGLONG curTime = CLRGetTickCount64();
934 if (curTime >= endTime)
935 {
936 return;
937 }
938 else
939 {
940 timeout = (DWORD)(endTime - curTime);
941 }
942 }
943 }
944 else
945 {
946 if (status == WAIT_TIMEOUT)
947 {
948 ULONGLONG finalizeStartTime = GetObjFinalizeStartTime();
949 if (finalizeStartTime)
950 {
951 if (CLRGetTickCount64() >= finalizeStartTime+timeout)
952 {
953 GCX_COOP();
954 FinalizerThreadAbortOnTimeout();
955 }
956 }
957 }
958 if (endTime != MAXULONGLONG)
959 {
960 ULONGLONG curTime = CLRGetTickCount64();
961 if (curTime >= endTime)
962 {
963 return;
964 }
965 }
966 }
967 }
968 }
969}
970
971
972#ifdef _DEBUG
973#define FINALIZER_WAIT_TIMEOUT 250
974#else
975#define FINALIZER_WAIT_TIMEOUT 200
976#endif
977#define FINALIZER_TOTAL_WAIT 2000
978
979static BOOL s_fRaiseExitProcessEvent = FALSE;
980static DWORD dwBreakOnFinalizeTimeOut = (DWORD) -1;
981
982static ULONGLONG ShutdownEnd;
983
984
985BOOL FinalizerThread::FinalizerThreadWatchDog()
986{
987 Thread *pThread = GetThread();
988
989 if (dwBreakOnFinalizeTimeOut == (DWORD) -1) {
990 dwBreakOnFinalizeTimeOut = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_BreakOnFinalizeTimeOut);
991 }
992
993 // Do not wait for FinalizerThread if the current one is FinalizerThread.
994 if (pThread == GetFinalizerThread())
995 return TRUE;
996
997 // If finalizer thread is gone, just return.
998 if (GetFinalizerThread()->Join (0, FALSE) != WAIT_TIMEOUT)
999 return TRUE;
1000
1001 // *** This is the first call ShutDown -> Finalizer to Finilize dead objects ***
1002 if ((g_fEEShutDown & ShutDown_Finalize1) &&
1003 !(g_fEEShutDown & ShutDown_Finalize2)) {
1004 ShutdownEnd = CLRGetTickCount64() + GetEEPolicy()->GetTimeout(OPR_ProcessExit);
1005 // Wait for the finalizer...
1006 LOG((LF_GC, LL_INFO10, "Signalling finalizer to quit..."));
1007
1008 fQuitFinalizer = TRUE;
1009 hEventFinalizerDone->Reset();
1010 EnableFinalization();
1011
1012 LOG((LF_GC, LL_INFO10, "Waiting for finalizer to quit..."));
1013
1014 if (pThread)
1015 {
1016 pThread->EnablePreemptiveGC();
1017 }
1018
1019 BOOL fTimeOut = FinalizerThreadWatchDogHelper();
1020
1021 if (!fTimeOut) {
1022 hEventShutDownToFinalizer->Set();
1023
1024 // Wait for finalizer thread to finish raising ExitProcess Event.
1025 s_fRaiseExitProcessEvent = TRUE;
1026 fTimeOut = FinalizerThreadWatchDogHelper();
1027 s_fRaiseExitProcessEvent = FALSE;
1028 }
1029
1030 if (pThread)
1031 {
1032 pThread->DisablePreemptiveGC();
1033 }
1034
1035 // Can not call ExitProcess here if we are in a hosting environment.
1036 // The host does not expect that we terminate the process.
1037 //if (fTimeOut)
1038 //{
1039 //::ExitProcess (GetLatchedExitCode());
1040 //}
1041
1042 return !fTimeOut;
1043 }
1044
1045 // *** This is the second call ShutDown -> Finalizer to ***
1046 // suspend the Runtime and Finilize live objects
1047 if ( g_fEEShutDown & ShutDown_Finalize2 &&
1048 !(g_fEEShutDown & ShutDown_COM) ) {
1049
1050#ifdef BACKGROUND_GC
1051 gc_heap::gc_can_use_concurrent = FALSE;
1052
1053 if (pGenGCHeap->settings.concurrent)
1054 pGenGCHeap->background_gc_wait();
1055#endif //BACKGROUND_GC
1056
1057 _ASSERTE((g_fEEShutDown & ShutDown_Finalize1) || g_fFastExitProcess);
1058
1059 if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_FinalizeOnShutdown) != 0)
1060 {
1061 // When running finalizers on shutdown (including for reachable objects), suspend threads for shutdown before
1062 // running finalizers, so that the reachable objects will not be used after they are finalized.
1063
1064 ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_SHUTDOWN);
1065
1066 g_fSuspendOnShutdown = TRUE;
1067
1068 // Do not balance the trap returning threads.
1069 // We are shutting down CLR. Only Finalizer/Shutdown threads can
1070 // return from DisablePreemptiveGC.
1071 ThreadStore::TrapReturningThreads(TRUE);
1072
1073 ThreadSuspend::RestartEE(FALSE, TRUE);
1074 }
1075
1076 if (g_fFastExitProcess)
1077 {
1078 return TRUE;
1079 }
1080
1081 // !!! Before we wake up Finalizer thread, we need to enable preemptive gc on the
1082 // !!! shutdown thread. Otherwise we may see a deadlock during debug test.
1083 if (pThread)
1084 {
1085 pThread->EnablePreemptiveGC();
1086 }
1087
1088 GCHeapUtilities::GetGCHeap()->SetFinalizeRunOnShutdown(true);
1089
1090 // Wait for finalizer thread to finish finalizing all objects.
1091 hEventShutDownToFinalizer->Set();
1092 BOOL fTimeOut = FinalizerThreadWatchDogHelper();
1093
1094 if (!fTimeOut) {
1095 GCHeapUtilities::GetGCHeap()->SetFinalizeRunOnShutdown(false);
1096 }
1097
1098 // Can not call ExitProcess here if we are in a hosting environment.
1099 // The host does not expect that we terminate the process.
1100 //if (fTimeOut) {
1101 // ::ExitProcess (GetLatchedExitCode());
1102 //}
1103
1104 if (pThread)
1105 {
1106 pThread->DisablePreemptiveGC();
1107 }
1108 return !fTimeOut;
1109 }
1110
1111 // *** This is the third call ShutDown -> Finalizer ***
1112 // to do additional cleanup
1113 if (g_fEEShutDown & ShutDown_COM) {
1114 _ASSERTE (g_fEEShutDown & (ShutDown_Finalize2 | ShutDown_Finalize1));
1115
1116 if (pThread)
1117 {
1118 pThread->EnablePreemptiveGC();
1119 }
1120
1121 GCHeapUtilities::GetGCHeap()->SetFinalizeRunOnShutdown(true);
1122
1123 hEventShutDownToFinalizer->Set();
1124 DWORD status = WAIT_OBJECT_0;
1125 while (CLREventWaitWithTry(hEventFinalizerToShutDown, FINALIZER_WAIT_TIMEOUT, TRUE, &status))
1126 {
1127 }
1128
1129 BOOL fTimeOut = (status == WAIT_TIMEOUT) ? TRUE : FALSE;
1130
1131 if (fTimeOut)
1132 {
1133 if (dwBreakOnFinalizeTimeOut) {
1134 LOG((LF_GC, LL_INFO10, "Finalizer took too long to clean up COM IP's.\n"));
1135 DebugBreak();
1136 }
1137 }
1138
1139 if (pThread)
1140 {
1141 pThread->DisablePreemptiveGC();
1142 }
1143
1144 return !fTimeOut;
1145 }
1146
1147 _ASSERTE(!"Should never reach this point");
1148 return FALSE;
1149}
1150
1151BOOL FinalizerThread::FinalizerThreadWatchDogHelper()
1152{
1153 // Since our thread is blocking waiting for the finalizer thread, we must be in preemptive GC
1154 // so that we don't in turn block the finalizer on us in a GC.
1155 Thread *pCurrentThread;
1156 pCurrentThread = GetThread();
1157 _ASSERTE (pCurrentThread == NULL || !pCurrentThread->PreemptiveGCDisabled());
1158
1159 // We're monitoring the finalizer thread.
1160 Thread *pThread = GetFinalizerThread();
1161 _ASSERTE(pThread != pCurrentThread);
1162
1163 ULONGLONG dwBeginTickCount = CLRGetTickCount64();
1164
1165 size_t prevCount;
1166 size_t curCount;
1167 BOOL fTimeOut = FALSE;
1168 DWORD nTry = 0;
1169 DWORD maxTotalWait = (DWORD)(ShutdownEnd - dwBeginTickCount);
1170 DWORD totalWaitTimeout;
1171 totalWaitTimeout = GetEEPolicy()->GetTimeout(OPR_FinalizerRun);
1172 if (totalWaitTimeout == (DWORD)-1)
1173 {
1174 totalWaitTimeout = FINALIZER_TOTAL_WAIT;
1175 }
1176
1177 if (s_fRaiseExitProcessEvent)
1178 {
1179 DWORD tmp = maxTotalWait/20; // Normally we assume 2 seconds timeout if total timeout is 40 seconds.
1180 if (tmp > totalWaitTimeout)
1181 {
1182 totalWaitTimeout = tmp;
1183 }
1184 prevCount = MAXLONG;
1185 }
1186 else
1187 {
1188 prevCount = GCHeapUtilities::GetGCHeap()->GetNumberOfFinalizable();
1189 }
1190
1191 DWORD maxTry = (DWORD)(totalWaitTimeout*1.0/FINALIZER_WAIT_TIMEOUT + 0.5);
1192 BOOL bAlertable = TRUE; //(g_fEEShutDown & ShutDown_Finalize2) ? FALSE:TRUE;
1193
1194 if (dwBreakOnFinalizeTimeOut == (DWORD) -1) {
1195 dwBreakOnFinalizeTimeOut = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_BreakOnFinalizeTimeOut);
1196 }
1197
1198 DWORD dwTimeout = FINALIZER_WAIT_TIMEOUT;
1199
1200 // This used to set the dwTimeout to infinite, but this can cause a hang when shutting down
1201 // if a finalizer tries to take a lock that another suspended managed thread already has.
1202 // This results in the hang because the other managed thread is never going to be resumed
1203 // because we're in shutdown. So we make a compromise here - make the timeout for every
1204 // iteration 10 times longer and make the total wait infinite - so if things hang we will
1205 // eventually shutdown but we also give things a chance to finish if they're running slower
1206 // because of the profiler.
1207#ifdef PROFILING_SUPPORTED
1208 if (CORProfilerPresent())
1209 {
1210 dwTimeout *= 10;
1211 maxTotalWait = INFINITE;
1212 }
1213#endif // PROFILING_SUPPORTED
1214
1215 // This change was added late in Windows Phone 8, so we want to keep it minimal.
1216 // We should consider refactoring this later, as we've got a lot of dead code here now on CoreCLR.
1217 dwTimeout = INFINITE;
1218 maxTotalWait = INFINITE;
1219
1220 while (1) {
1221 struct Param
1222 {
1223 DWORD status;
1224 DWORD dwTimeout;
1225 BOOL bAlertable;
1226 } param;
1227 param.status = 0;
1228 param.dwTimeout = dwTimeout;
1229 param.bAlertable = bAlertable;
1230
1231 PAL_TRY(Param *, pParam, &param)
1232 {
1233 pParam->status = hEventFinalizerToShutDown->Wait(pParam->dwTimeout, pParam->bAlertable);
1234 }
1235 PAL_EXCEPT (EXCEPTION_EXECUTE_HANDLER)
1236 {
1237 param.status = WAIT_TIMEOUT;
1238 }
1239 PAL_ENDTRY
1240
1241 if (param.status != WAIT_TIMEOUT) {
1242 break;
1243 }
1244 nTry ++;
1245 // ExitProcessEventCount is incremental
1246 // FinalizableObjects is decremental
1247 if (s_fRaiseExitProcessEvent)
1248 {
1249 curCount = MAXLONG - GetProcessedExitProcessEventCount();
1250 }
1251 else
1252 {
1253 curCount = GCHeapUtilities::GetGCHeap()->GetNumberOfFinalizable();
1254 }
1255
1256 if ((prevCount <= curCount)
1257 && !GCHeapUtilities::GetGCHeap()->ShouldRestartFinalizerWatchDog()
1258 && (pThread == NULL || !(pThread->m_State & (Thread::TS_UserSuspendPending | Thread::TS_DebugSuspendPending)))){
1259 if (nTry == maxTry) {
1260 if (!s_fRaiseExitProcessEvent) {
1261 LOG((LF_GC, LL_INFO10, "Finalizer took too long on one object.\n"));
1262 }
1263 else
1264 LOG((LF_GC, LL_INFO10, "Finalizer took too long to process ExitProcess event.\n"));
1265
1266 fTimeOut = TRUE;
1267 if (dwBreakOnFinalizeTimeOut != 2) {
1268 break;
1269 }
1270 }
1271 }
1272 else
1273 {
1274 nTry = 0;
1275 prevCount = curCount;
1276 }
1277 ULONGLONG dwCurTickCount = CLRGetTickCount64();
1278 if (pThread && pThread->m_State & (Thread::TS_UserSuspendPending | Thread::TS_DebugSuspendPending)) {
1279 // CoreCLR does not support user-requested thread suspension
1280 _ASSERTE(!(pThread->m_State & Thread::TS_UserSuspendPending));
1281 dwBeginTickCount = dwCurTickCount;
1282 }
1283 if (dwCurTickCount - dwBeginTickCount >= maxTotalWait)
1284 {
1285 LOG((LF_GC, LL_INFO10, "Finalizer took too long on shutdown.\n"));
1286 fTimeOut = TRUE;
1287 if (dwBreakOnFinalizeTimeOut != 2) {
1288 break;
1289 }
1290 }
1291 }
1292
1293 if (fTimeOut)
1294 {
1295 if (dwBreakOnFinalizeTimeOut){
1296 DebugBreak();
1297 }
1298 }
1299
1300 return fTimeOut;
1301}
1302