@@ -872,6 +872,10 @@ void EEStartupHelper()
872
872
}
873
873
#endif
874
874
875
+ // This isn't done as part of InitializeGarbageCollector() above because
876
+ // debugger must be initialized before creating EE thread objects
877
+ FinalizerThread::FinalizerThreadCreate ();
878
+
875
879
InitPreStubManager ();
876
880
877
881
#ifdef FEATURE_COMINTEROP
@@ -909,10 +913,6 @@ void EEStartupHelper()
909
913
#endif // FEATURE_PERFTRACING
910
914
GenAnalysis::Initialize ();
911
915
912
- // This isn't done as part of InitializeGarbageCollector() above because thread
913
- // creation requires AppDomains to have been set up.
914
- FinalizerThread::FinalizerThreadCreate ();
915
-
916
916
// Now we really have fully initialized the garbage collector
917
917
SetGarbageCollectorFullyInitialized ();
918
918
@@ -964,6 +964,12 @@ void EEStartupHelper()
964
964
g_MiniMetaDataBuffMaxSize, MEM_COMMIT, PAGE_READWRITE);
965
965
#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
966
966
967
+ #ifdef TARGET_WINDOWS
968
+ // By now finalizer thread should have initialized FLS slot for thread cleanup notifications.
969
+ // And ensured that COM is initialized (must happen before allocating FLS slot).
970
+ // Make sure that this was done.
971
+ FinalizerThread::WaitForFinalizerThreadStart ();
972
+ #endif
967
973
g_fEEStarted = TRUE ;
968
974
g_EEStartupStatus = S_OK;
969
975
hr = S_OK;
@@ -1684,6 +1690,135 @@ BOOL STDMETHODCALLTYPE EEDllMain( // TRUE on success, FALSE on error.
1684
1690
1685
1691
#endif // !defined(CORECLR_EMBEDDED)
1686
1692
1693
+ static void RuntimeThreadShutdown (void * thread)
1694
+ {
1695
+ Thread* pThread = (Thread*)thread;
1696
+ _ASSERTE (pThread == GetThreadNULLOk ());
1697
+
1698
+ if (pThread)
1699
+ {
1700
+ #ifdef FEATURE_COMINTEROP
1701
+ // reset the CoInitialize state
1702
+ // so we don't call CoUninitialize during thread detach
1703
+ pThread->ResetCoInitialized ();
1704
+ #endif // FEATURE_COMINTEROP
1705
+ // For case where thread calls ExitThread directly, we need to reset the
1706
+ // frame pointer. Otherwise stackwalk would AV. We need to do it in cooperative mode.
1707
+ // We need to set m_GCOnTransitionsOK so this thread won't trigger GC when toggle GC mode
1708
+ if (pThread->m_pFrame != FRAME_TOP)
1709
+ {
1710
+ #ifdef _DEBUG
1711
+ pThread->m_GCOnTransitionsOK = FALSE ;
1712
+ #endif
1713
+ GCX_COOP_NO_DTOR ();
1714
+ pThread->m_pFrame = FRAME_TOP;
1715
+ GCX_COOP_NO_DTOR_END ();
1716
+ }
1717
+
1718
+ pThread->DetachThread (TRUE );
1719
+ }
1720
+ else
1721
+ {
1722
+ // Since we don't actually cleanup the TLS data along this path, verify that it is already cleaned up
1723
+ AssertThreadStaticDataFreed ();
1724
+ }
1725
+
1726
+ ThreadDetaching ();
1727
+ }
1728
+
1729
+ #ifdef TARGET_WINDOWS
1730
+
1731
+ // Index for the fiber local storage of the attached thread pointer
1732
+ static uint32_t g_flsIndex = FLS_OUT_OF_INDEXES;
1733
+
1734
+ #define FLS_STATE_CLEAR 0
1735
+ #define FLS_STATE_ARMED 1
1736
+ #define FLS_STATE_INVOKED 2
1737
+
1738
+ static __declspec (thread) byte t_flsState;
1739
+
1740
+ // This is called when each *fiber* is destroyed. When the home fiber of a thread is destroyed,
1741
+ // it means that the thread itself is destroyed.
1742
+ // Since we receive that notification outside of the Loader Lock, it allows us to safely acquire
1743
+ // the ThreadStore lock in the RuntimeThreadShutdown.
1744
+ static void __stdcall FiberDetachCallback (void * lpFlsData)
1745
+ {
1746
+ _ASSERTE (g_flsIndex != FLS_OUT_OF_INDEXES);
1747
+ _ASSERTE (lpFlsData);
1748
+
1749
+ if (t_flsState == FLS_STATE_ARMED)
1750
+ {
1751
+ RuntimeThreadShutdown (lpFlsData);
1752
+ }
1753
+
1754
+ t_flsState = FLS_STATE_INVOKED;
1755
+ }
1756
+
1757
+ void InitFlsSlot ()
1758
+ {
1759
+ // We use fiber detach callbacks to run our thread shutdown code because the fiber detach
1760
+ // callback is made without the OS loader lock
1761
+ g_flsIndex = FlsAlloc (FiberDetachCallback);
1762
+ if (g_flsIndex == FLS_OUT_OF_INDEXES)
1763
+ {
1764
+ COMPlusThrowWin32 ();
1765
+ }
1766
+ }
1767
+
1768
+ // Register the thread with OS to be notified when thread is about to be destroyed
1769
+ // It fails fast if a different thread was already registered with the current fiber.
1770
+ // Parameters:
1771
+ // thread - thread to attach
1772
+ static void OsAttachThread (void * thread)
1773
+ {
1774
+ if (t_flsState == FLS_STATE_INVOKED)
1775
+ {
1776
+ _ASSERTE_ALL_BUILDS (!" Attempt to execute managed code after the .NET runtime thread state has been destroyed." );
1777
+ }
1778
+
1779
+ t_flsState = FLS_STATE_ARMED;
1780
+
1781
+ // Associate the current fiber with the current thread. This makes the current fiber the thread's "home"
1782
+ // fiber. This fiber is the only fiber allowed to execute managed code on this thread. When this fiber
1783
+ // is destroyed, we consider the thread to be destroyed.
1784
+ _ASSERTE (thread != NULL );
1785
+ FlsSetValue (g_flsIndex, thread);
1786
+ }
1787
+
1788
+ // Detach thread from OS notifications.
1789
+ // It fails fast if some other thread value was attached to the current fiber.
1790
+ // Parameters:
1791
+ // thread - thread to detach
1792
+ void OsDetachThread (void * thread)
1793
+ {
1794
+ ASSERT (g_flsIndex != FLS_OUT_OF_INDEXES);
1795
+ void * threadFromCurrentFiber = FlsGetValue (g_flsIndex);
1796
+
1797
+ if (threadFromCurrentFiber == NULL )
1798
+ {
1799
+ // Thread is not attached.
1800
+ // This could come from DestroyThread called when refcount reaches 0
1801
+ // and the thread may have already been detached or never attached.
1802
+ // We leave t_flsState as-is to keep track whether our callback has been called.
1803
+ return ;
1804
+ }
1805
+
1806
+ if (threadFromCurrentFiber != thread)
1807
+ {
1808
+ _ASSERTE_ALL_BUILDS (!" Detaching a thread from the wrong fiber" );
1809
+ }
1810
+
1811
+ // Leave the existing FLS value, to keep the callback "armed" so that we could observe the termination callback.
1812
+ // After that we will not allow to attach as we will no longer be able to clean up.
1813
+ t_flsState = FLS_STATE_CLEAR;
1814
+ }
1815
+
1816
+ void EnsureTlsDestructionMonitor ()
1817
+ {
1818
+ OsAttachThread (GetThread ());
1819
+ }
1820
+
1821
+ #else
1687
1822
struct TlsDestructionMonitor
1688
1823
{
1689
1824
bool m_activated = false ;
@@ -1697,36 +1832,7 @@ struct TlsDestructionMonitor
1697
1832
{
1698
1833
if (m_activated)
1699
1834
{
1700
- Thread* thread = GetThreadNULLOk ();
1701
- if (thread)
1702
- {
1703
- #ifdef FEATURE_COMINTEROP
1704
- // reset the CoInitialize state
1705
- // so we don't call CoUninitialize during thread detach
1706
- thread->ResetCoInitialized ();
1707
- #endif // FEATURE_COMINTEROP
1708
- // For case where thread calls ExitThread directly, we need to reset the
1709
- // frame pointer. Otherwise stackwalk would AV. We need to do it in cooperative mode.
1710
- // We need to set m_GCOnTransitionsOK so this thread won't trigger GC when toggle GC mode
1711
- if (thread->m_pFrame != FRAME_TOP)
1712
- {
1713
- #ifdef _DEBUG
1714
- thread->m_GCOnTransitionsOK = FALSE ;
1715
- #endif
1716
- GCX_COOP_NO_DTOR ();
1717
- thread->m_pFrame = FRAME_TOP;
1718
- GCX_COOP_NO_DTOR_END ();
1719
- }
1720
-
1721
- thread->DetachThread (TRUE );
1722
- }
1723
- else
1724
- {
1725
- // Since we don't actually cleanup the TLS data along this path, verify that it is already cleaned up
1726
- AssertThreadStaticDataFreed ();
1727
- }
1728
-
1729
- ThreadDetaching ();
1835
+ RuntimeThreadShutdown (GetThreadNULLOk ());
1730
1836
}
1731
1837
}
1732
1838
};
@@ -1740,6 +1846,8 @@ void EnsureTlsDestructionMonitor()
1740
1846
tls_destructionMonitor.Activate ();
1741
1847
}
1742
1848
1849
+ #endif
1850
+
1743
1851
#ifdef DEBUGGING_SUPPORTED
1744
1852
//
1745
1853
// InitializeDebugger initialized the Runtime-side CLR Debugging Services
0 commit comments