Skip to content

Release debugger thread tracker on the win32 event thread#119776

Open
tommcdon wants to merge 2 commits into
dotnet:mainfrom
tommcdon:dev/tommcdon/fix_debug_tracker_cleanup
Open

Release debugger thread tracker on the win32 event thread#119776
tommcdon wants to merge 2 commits into
dotnet:mainfrom
tommcdon:dev/tommcdon/fix_debug_tracker_cleanup

Conversation

@tommcdon

Copy link
Copy Markdown
Member

The m_unmanagedThreadHashTable in CordbProcess is used to track unmanaged threads for out-of-process setthreadcontext, suspending/resuming threads for single-stepping over call instructions. The table is accessed view the win32 event thread. When neutering CordbProcess, it's still possible to be receiving win32 events in parallel, however, we currently iterate and release objects in m_unmanagedThreadHashTable on a separate thread. This could cause issues with the objects, or even a crash. This change moves the deallocation of the objects in m_unmanagedThreadHashTable to the win32 event thread, eliminating thread unsafeness.

@tommcdon tommcdon self-assigned this Sep 16, 2025
@tommcdon tommcdon added this to the 11.0.0 milestone Sep 16, 2025

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR addresses a thread safety issue in the CoreCLR debugger by moving the cleanup of unmanaged thread trackers from the CordbProcess::Neuter() method to the win32 event thread during process exit. The change prevents potential crashes that could occur when neutering the process while win32 events are still being processed in parallel.

Key changes:

  • Moved thread tracker cleanup logic from Neuter() to win32 event thread execution
  • Added a new ClearThreadTrackers() method to encapsulate the cleanup logic
  • Ensured thread-safe access to the unmanaged thread hash table

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/coreclr/debug/di/rspriv.h Added declaration for new ClearThreadTrackers() method
src/coreclr/debug/di/process.cpp Removed thread tracker cleanup from Neuter(), added new method implementation, and called it from win32 event thread

m_pProcess->m_exiting = true;

#ifdef OUT_OF_PROCESS_SETTHREADCONTEXT
m_pProcess->ClearThreadTrackers(); // we must release the thread trackers on the Win32EventThread

@hoyosjs hoyosjs Sep 16, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CordbProcess::Neuter ensured that threads were resumed and let go from the tracker. Now, on detach we won't clean them out. Could this leave threads in suspended state on detach?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For process exit, the threads are gone anyway. I have a separate PR solves the problem for detach. #118849

@xtqqczze

Copy link
Copy Markdown
Contributor

Build failures were resolved by 579bd79.

@thaystg thaystg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@github-actions

github-actions Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Workflow state for the Holistic Review Orchestrator.

{
  "version": 5,
  "last_dispatched_commit": "a3f00614b9eb05422e61f2ebc8eb18cc736d8e80",
  "last_dispatched_base_ref": "main",
  "last_dispatched_base_sha": "3fc2c4afb24f6b005cf91bfdc3d1ed06e956563e",
  "last_reviewed_commit": "a3f00614b9eb05422e61f2ebc8eb18cc736d8e80",
  "last_reviewed_base_ref": "main",
  "last_reviewed_base_sha": "3fc2c4afb24f6b005cf91bfdc3d1ed06e956563e",
  "last_recorded_worker_run_id": "29673722851",
  "review_attempt_commit": "",
  "review_attempt_base_ref": "",
  "review_attempt_count": 0,
  "max_review_attempts": 5,
  "review_history_format": "holistic-review-disclosure-v1",
  "review_history": [
    {
      "commit": "a3f00614b9eb05422e61f2ebc8eb18cc736d8e80",
      "review_id": 4730106436
    }
  ]
}

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Holistic Review

Motivation: m_unmanagedThreadHashTable on CordbProcess tracks unmanaged threads used for out-of-process SetThreadContext single-stepping. It is normally accessed on the Win32 event thread. Previously CordbProcess::Neuter() iterated the table and deleted the UnmanagedThreadTracker objects, but Neuter() can run on a different thread while the Win32 event thread is still processing native debug events, creating a data race that can corrupt these objects or crash the debugger.

Approach: The deletion logic is extracted from Neuter() into a new CordbProcess::ClearThreadTrackers() method, which is now invoked from CordbWin32EventThread::ExitProcess() under the process lock, guaranteeing it runs on the Win32 event thread. The moved code is otherwise identical (Close + delete each tracker, RemoveAll(), reset m_dwOutOfProcessStepping), and both the old and new sites are guarded by OUT_OF_PROCESS_SETTHREADCONTEXT. The header declares the new method inside the same conditional block.

Summary: The change is minimal, correct, and directly addresses the described race. The cleanup now happens on the owning thread and under m_pProcess->Lock(), which is the right synchronization boundary. I found no correctness or resource-management regressions in the changed lines. One non-blocking observation: ExitProcess() has early-return paths (the DebugActiveProcessStop failure path and the EP(detach) after EP(exit) path) that occur before ClearThreadTrackers() is reached. The detach-failure early return leaves the process alive so no leak results there, and the second case is a duplicate call whose first invocation already cleared the table, so neither introduces a leak in practice. Verdict: LGTM.

Note

This review was generated by this repository's Holistic Review agentic workflow to complement the built-in Copilot review.

Generated by Holistic Review · 48.1 AIC · ⌖ 10.4 AIC · ⊞ 10K

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants