Add debug assert to detect CRST_DEFAULT locks taken under ANYMODE locks#128912
Open
davidwrighton wants to merge 1 commit into
Open
Add debug assert to detect CRST_DEFAULT locks taken under ANYMODE locks#128912davidwrighton wants to merge 1 commit into
davidwrighton wants to merge 1 commit into
Conversation
Add a thread_local counter that tracks how many CRST_UNSAFE_ANYMODE (and SimpleRWLock COOPERATIVE_OR_PREEMPTIVE) locks are held on the current thread. When acquiring a CRST_DEFAULT lock or a PREEMPTIVE SimpleRWLock, assert that the counter is zero. Taking a GC_TRIGGERS lock while holding an ANYMODE lock is illegal because it may cause a deadlock: if another thread attempts to acquire the ANYMODE lock from cooperative mode while the DEFAULT lock is transitioning between GC modes, and a third thread triggers a GC, all three threads can deadlock. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
|
Tagging subscribers to this area: @agocke |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds new debug-only invariants in CoreCLR’s VM locking infrastructure to detect a dangerous lock-ordering pattern: acquiring a GC_TRIGGERS-style lock (CRST default / PREEMPTIVE SimpleRWLock) while an ANYMODE-style lock is already held on the same thread.
Changes:
- Introduces a debug-only
thread_localper-thread counter to track “anymode/no-trigger” locks held. - Adds debug asserts in
CrstBase::Enter()andSimpleRWLock::EnterRead/EnterWrite()to fail fast when taking GC_TRIGGERS-style locks under those locks. - Updates
CrstBaseandSimpleRWLockenter/leave paths to increment/decrement the counter.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| src/coreclr/vm/simplerwlock.hpp | Declares the debug-only t_unsafeAnyModeHeldCount for SimpleRWLock codepaths. |
| src/coreclr/vm/simplerwlock.cpp | Adds PREEMPTIVE acquisition asserts and updates the counter for COOPERATIVE_OR_PREEMPTIVE lock enter/leave. |
| src/coreclr/vm/crst.h | Declares the debug-only t_unsafeAnyModeHeldCount for CRST codepaths. |
| src/coreclr/vm/crst.cpp | Defines the counter and adds the CRST default-lock assert plus counter maintenance for CRST_UNSAFE_ANYMODE locks. |
Comment on lines
+304
to
+307
| if (m_dwFlags & CRST_UNSAFE_ANYMODE) | ||
| { | ||
| t_unsafeAnyModeHeldCount--; | ||
| } |
Comment on lines
+304
to
+307
| if (m_gcMode == COOPERATIVE_OR_PREEMPTIVE) | ||
| { | ||
| t_unsafeAnyModeHeldCount--; | ||
| } |
Comment on lines
+99
to
+101
| // Per-thread count of CRST_UNSAFE_ANYMODE (and equivalent) locks currently held. | ||
| // Used to detect illegal acquisition of GC_TRIGGERS locks under ANYMODE locks. | ||
| extern thread_local int t_unsafeAnyModeHeldCount; |
Comment on lines
+50
to
+53
| _ASSERTE_MSG(t_unsafeAnyModeHeldCount == 0, | ||
| "Taking a PREEMPTIVE SimpleRWLock while a COOPERATIVE_OR_PREEMPTIVE lock or " | ||
| "CRST_UNSAFE_ANYMODE lock is held is illegal. " | ||
| "The PREEMPTIVE lock may trigger a GC, which is not allowed under ANYMODE locks."); |
Comment on lines
+151
to
+154
| _ASSERTE_MSG(t_unsafeAnyModeHeldCount == 0, | ||
| "Taking a PREEMPTIVE SimpleRWLock while a COOPERATIVE_OR_PREEMPTIVE lock or " | ||
| "CRST_UNSAFE_ANYMODE lock is held is illegal. " | ||
| "The PREEMPTIVE lock may trigger a GC, which is not allowed under ANYMODE locks."); |
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note
This PR description was generated with the assistance of GitHub Copilot.
Summary
Adds a debug-only check that detects when a
CRST_DEFAULTlock (orPREEMPTIVESimpleRWLock) is acquired while aCRST_UNSAFE_ANYMODElock (orCOOPERATIVE_OR_PREEMPTIVESimpleRWLock) is already held on the same thread.Motivation
Taking a GC_TRIGGERS lock while holding an ANYMODE lock can cause a three-way deadlock:
All three threads can deadlock waiting on each other.
Changes
thread_local int t_unsafeAnyModeHeldCountcounter (debug-only) that tracks how many ANYMODE-style locks are held on the current thread.CrstBase::Enter()increments the counter when acquiring aCRST_UNSAFE_ANYMODElock and asserts it is zero when acquiring a default lock.CrstBase::Leave()decrements the counter when releasing aCRST_UNSAFE_ANYMODElock.SimpleRWLock::PostEnter()increments the counter forCOOPERATIVE_OR_PREEMPTIVElocks.SimpleRWLock::PreLeave()decrements the counter forCOOPERATIVE_OR_PREEMPTIVElocks.SimpleRWLock::EnterRead()andEnterWrite()assert the counter is zero forPREEMPTIVElocks.All changes are guarded by
#ifdef _DEBUGand have no effect on release builds.