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 | |
20 | BOOL FinalizerThread::fRunFinalizersOnUnload = FALSE; |
21 | BOOL FinalizerThread::fQuitFinalizer = FALSE; |
22 | |
23 | #if defined(__linux__) && defined(FEATURE_EVENT_TRACE) |
24 | #define LINUX_HEAP_DUMP_TIME_OUT 10000 |
25 | |
26 | extern bool s_forcedGCInProgress; |
27 | ULONGLONG FinalizerThread::LastHeapDumpTime = 0; |
28 | |
29 | Volatile<BOOL> g_TriggerHeapDump = FALSE; |
30 | #endif // __linux__ |
31 | |
32 | CLREvent * FinalizerThread::hEventFinalizer = NULL; |
33 | CLREvent * FinalizerThread::hEventFinalizerDone = NULL; |
34 | CLREvent * FinalizerThread::hEventShutDownToFinalizer = NULL; |
35 | CLREvent * FinalizerThread::hEventFinalizerToShutDown = NULL; |
36 | |
37 | HANDLE FinalizerThread::MHandles[kHandleCount]; |
38 | |
39 | BOOL FinalizerThread::IsCurrentThreadFinalizer() |
40 | { |
41 | LIMITED_METHOD_CONTRACT; |
42 | |
43 | return GetThread() == g_pFinalizerThread; |
44 | } |
45 | |
46 | void FinalizerThread::EnableFinalization() |
47 | { |
48 | WRAPPER_NO_CONTRACT; |
49 | |
50 | hEventFinalizer->Set(); |
51 | } |
52 | |
53 | BOOL FinalizerThread::() |
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) |
63 | void 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 | |
75 | void 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 | |
108 | struct FinalizeAllObjects_Args { |
109 | OBJECTREF fobj; |
110 | int bitToCheck; |
111 | }; |
112 | |
113 | void 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. |
130 | static struct ManagedThreadCallState *pThreadTurnAround; |
131 | |
132 | Object * 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 | |
216 | Object * 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 |
307 | void 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 | |
364 | void 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 | |
496 | static BOOL s_FinalizerThreadOK = FALSE; |
497 | |
498 | |
499 | |
500 | VOID 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. |
624 | void 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 | |
637 | DWORD 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 | |
814 | void 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 | |
863 | void 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. |
875 | void 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 | |
979 | static BOOL s_fRaiseExitProcessEvent = FALSE; |
980 | static DWORD dwBreakOnFinalizeTimeOut = (DWORD) -1; |
981 | |
982 | static ULONGLONG ShutdownEnd; |
983 | |
984 | |
985 | BOOL 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 | |
1151 | BOOL 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, ¶m) |
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 | |