Repository: dotnet/runtime
File: src/coreclr/vm/threads.cpp (C++)

Code before:
#endif // FEATURE_INTERPRETER

#ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT
    // If the currently running thread is the thread that died and it is an STA thread, then we
    // need to release all the RCW's in the current context. However, we cannot do this if we
    // are in the middle of process detach.
    if (!IsAtProcessExit() && this == GetThreadNULLOk())
    {
        CleanupCOMState();
    }
#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT

    if (this == GetThreadNULLOk())
    {
        CooperativeCleanup();
    }

    if (g_fEEShutDown)
    {
        // We have started shutdown.  Not safe to touch CLR state.
        return;
    }

    // We took a count during construction, and we rely on the count being
    // non-zero as we terminate the thread here.
    _ASSERTE(m_ExternalRefCount > 0);

    // The thread is no longer running.  It's important that we zero any general OBJECTHANDLE's
    // on this Thread object.  That's because we need the managed Thread object to be subject to
    // GC and yet any HANDLE is opaque to the GC when it comes to collecting cycles. When the
    // thread is executing, nothing can be collected anyway.  But now that we stop running the
    // cycle concerns us.
    //
    // It's important that we only use OBJECTHANDLE's that are retrievable while the thread is
    // still running.  That's what allows us to zero them here with impunity:
    {
        // No handles to clean up in the m_ExceptionState
        _ASSERTE(!m_ExceptionState.IsExceptionInProgress());

        GCX_COOP();

        // Destroy the LastThrown handle (and anything that violates the above assert).
        SafeSetThrowables(NULL);

        // Free loader allocator structures related to this thread
        FreeLoaderAllocatorHandlesForTLSData(this);
    }

    // We switch a thread to dead when it has finished doing useful work.  But it
    // remains in the thread store so long as someone keeps it alive.  An exposed
    // object will do this (it releases the refcount in its finalizer).  If the

Review comment (performance):
The deletion of m_pInterpThreadContext has been moved to after the g_fEEShutDown check, which means it will no longer be deleted during shutdown (the early return at line 2722 will skip it). In the original code, this deletion occurred before the shutdown check and would always execute. This change might lead to memory leaks in leak detection tools during shutdown scenarios, though it won't affect normal execution since the process is terminating. Consider whether this deletion should occur before or after the shutdown check based on whether cleanup is needed for leak detection.

Code after:
    {
        CleanupCOMState();
    }
#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT

    if (this == GetThreadNULLOk())
    {
        CooperativeCleanup();
    }

    if (g_fEEShutDown)
    {
        // We have started shutdown.  Not safe to touch CLR state.
        return;
    }

    // We took a count during construction, and we rely on the count being
    // non-zero as we terminate the thread here.
    _ASSERTE(m_ExternalRefCount > 0);

#ifdef FEATURE_INTERPRETER
    if (m_pInterpThreadContext != nullptr)
    {
        delete m_pInterpThreadContext;
    }
#endif // FEATURE_INTERPRETER

    // The thread is no longer running.  It's important that we zero any general OBJECTHANDLE's
    // on this Thread object.  That's because we need the managed Thread object to be subject to
    // GC and yet any HANDLE is opaque to the GC when it comes to collecting cycles. When the
    // thread is executing, nothing can be collected anyway.  But now that we stop running the
    // cycle concerns us.
    //
    // It's important that we only use OBJECTHANDLE's that are retrievable while the thread is
    // still running.  That's what allows us to zero them here with impunity:
    {
        // No handles to clean up in the m_ExceptionState
        _ASSERTE(!m_ExceptionState.IsExceptionInProgress());

        GCX_COOP();

        // Destroy the LastThrown handle (and anything that violates the above assert).
        SafeSetThrowables(NULL);

        // Free loader allocator structures related to this thread
        FreeLoaderAllocatorHandlesForTLSData(this);
    }

    // We switch a thread to dead when it has finished doing useful work.  But it
    // remains in the thread store so long as someone keeps it alive.  An exposed
    // object will do this (it releases the refcount in its finalizer).  If the