@@ -443,25 +443,25 @@ static void UninitDLL()
443443#endif // PROFILE_STARTUP
444444}
445445
446- volatile Thread* g_threadPerformingShutdown = NULL ;
447-
448- static void DllThreadDetach ()
446+ #ifdef _WIN32
447+ // This is set to the thread that initiates and performs the shutdown and may run
448+ // after other threads are rudely terminated. So far this is a Windows-specific concern.
449+ //
450+ // On POSIX OSes a process typically lives as long as any of its threads are alive or until
451+ // the process is terminated via `exit()` or a signal. Thus there is no such distinction
452+ // between threads.
453+ Thread* g_threadPerformingShutdown = NULL ;
454+
455+ static void __cdecl OnProcessExit ()
449456{
450- // BEWARE: loader lock is held here!
451-
452- // Should have already received a call to FiberDetach for this thread's "home" fiber.
453- Thread* pCurrentThread = ThreadStore::GetCurrentThreadIfAvailable ();
454- if (pCurrentThread != NULL && !pCurrentThread->IsDetached ())
455- {
456- // Once shutdown starts, RuntimeThreadShutdown callbacks are ignored, implying that
457- // it is no longer guaranteed that exiting threads will be detached.
458- if (g_threadPerformingShutdown != NULL )
459- {
460- ASSERT_UNCONDITIONALLY (" Detaching thread whose home fiber has not been detached" );
461- RhFailFast ();
462- }
463- }
457+ // The process is exiting and the current thread is performing the shutdown.
458+ // When this thread exits some threads may be already rudely terminated.
459+ // It would not be a good idea for this thread to wait on any locks
460+ // or run managed code at shutdown, so we will not try detaching it.
461+ Thread* currentThread = ThreadStore::RawGetCurrentThread ();
462+ g_threadPerformingShutdown = currentThread;
464463}
464+ #endif
465465
466466void RuntimeThreadShutdown (void * thread)
467467{
@@ -470,35 +470,38 @@ void RuntimeThreadShutdown(void* thread)
470470 // that is made for the single thread that runs the final stages of orderly process
471471 // shutdown (i.e., the thread that delivers the DLL_PROCESS_DETACH notifications when the
472472 // process is being torn down via an ExitProcess call).
473+ // In such case we do not detach.
473474
474- UNREFERENCED_PARAMETER (thread);
475+ #ifdef _WIN32
476+ ASSERT ((Thread*)thread == ThreadStore::GetCurrentThread ());
475477
476- #ifdef TARGET_UNIX
477- // Some Linux toolset versions call thread-local destructors during shutdown on a wrong thread.
478- if ((Thread*)thread != ThreadStore::GetCurrentThread ())
478+ // Do not try detaching the thread that performs the shutdown.
479+ if (g_threadPerformingShutdown == thread)
479480 {
481+ // At this point other threads could be terminated rudely while leaving runtime
482+ // in inconsistent state, so we would be risking blocking the process from exiting.
480483 return ;
481484 }
482485#else
483- ASSERT ((Thread*)thread == ThreadStore::GetCurrentThread ());
484-
485- // Do not do shutdown for the thread that performs the shutdown.
486- // other threads could be terminated before it and could leave TLS locked
487- if ((Thread*)thread == g_threadPerformingShutdown)
486+ // Some Linux toolset versions call thread-local destructors during shutdown on a wrong thread.
487+ if ((Thread*)thread != ThreadStore::GetCurrentThread ())
488488 {
489489 return ;
490490 }
491-
492491#endif
493492
494- ThreadStore::DetachCurrentThread (g_threadPerformingShutdown != NULL );
493+ ThreadStore::DetachCurrentThread ();
495494}
496495
497496extern " C" bool RhInitialize ()
498497{
499498 if (!PalInit ())
500499 return false ;
501500
501+ #ifdef _WIN32
502+ atexit (&OnProcessExit);
503+ #endif
504+
502505 if (!InitDLL (PalGetModuleHandleFromPointer ((void *)&RhInitialize)))
503506 return false ;
504507
@@ -508,47 +511,4 @@ extern "C" bool RhInitialize()
508511 return true ;
509512}
510513
511- //
512- // Currently called only from a managed executable once Main returns, this routine does whatever is needed to
513- // cleanup managed state before exiting. There's not a lot here at the moment since we're always about to let
514- // the OS tear the process down anyway.
515- //
516- // @TODO: Eventually we'll probably have a hosting API and explicit shutdown request. When that happens we'll
517- // something more sophisticated here since we won't be able to rely on the OS cleaning up after us.
518- //
519- COOP_PINVOKE_HELPER (void , RhpShutdown, ())
520- {
521- // Indicate that runtime shutdown is complete and that the caller is about to start shutting down the entire process.
522- g_threadPerformingShutdown = ThreadStore::RawGetCurrentThread ();
523- }
524-
525- #ifdef _WIN32
526- EXTERN_C UInt32_BOOL WINAPI RtuDllMain (HANDLE hPalInstance, uint32_t dwReason, void * pvReserved)
527- {
528- switch (dwReason)
529- {
530- case DLL_PROCESS_ATTACH:
531- {
532- STARTUP_TIMELINE_EVENT (PROCESS_ATTACH_BEGIN);
533-
534- if (!InitDLL (hPalInstance))
535- return FALSE ;
536-
537- STARTUP_TIMELINE_EVENT (PROCESS_ATTACH_COMPLETE);
538- }
539- break ;
540-
541- case DLL_PROCESS_DETACH:
542- UninitDLL ();
543- break ;
544-
545- case DLL_THREAD_DETACH:
546- DllThreadDetach ();
547- break ;
548- }
549-
550- return TRUE ;
551- }
552- #endif // _WIN32
553-
554514#endif // !DACCESS_COMPILE
0 commit comments