diff --git a/src/coreclr/vm/syncblk.inl b/src/coreclr/vm/syncblk.inl index 05753058577227..eb7a96fae45608 100644 --- a/src/coreclr/vm/syncblk.inl +++ b/src/coreclr/vm/syncblk.inl @@ -632,10 +632,10 @@ FORCEINLINE AwareLock::EnterHelperResult ObjHeader::EnterObjMonitorHelper(Thread return AwareLock::EnterHelperResult_Contention; } - // The header is transitioning - treat this as if the lock was taken + // The header is transitioning - use the slow path if (oldValue & BIT_SBLK_SPIN_LOCK) { - return AwareLock::EnterHelperResult_Contention; + return AwareLock::EnterHelperResult_UseSlowPath; } // Here we know we have the "thin lock" layout, but the lock is not free. diff --git a/src/libraries/System.Threading/tests/MonitorTests.cs b/src/libraries/System.Threading/tests/MonitorTests.cs index a00dd5546051f3..b596522385e73b 100644 --- a/src/libraries/System.Threading/tests/MonitorTests.cs +++ b/src/libraries/System.Threading/tests/MonitorTests.cs @@ -456,5 +456,35 @@ public static void Enter_HasToWait_LockContentionCountTest() Enter_HasToWait(); Assert.True(Monitor.LockContentionCount - initialLockContentionCount >= 2); } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] + public static void ObjectHeaderSyncBlockTransitionTryEnterRaceTest() + { + var threadStarted = new AutoResetEvent(false); + var startTest = new AutoResetEvent(false); + var obj = new object(); + var t = ThreadTestHelpers.CreateGuardedThread(out _, () => + { + threadStarted.Set(); + startTest.CheckedWait(); + Monitor.TryEnter(obj, 100); // likely to perform a full wait, which may involve some sort of transition + }); + t.IsBackground = true; + t.Start(); + threadStarted.CheckedWait(); + + lock (obj) + { + startTest.Set(); + do + { + for (int i = 0; i < 1000; i++) + { + Assert.True(Monitor.TryEnter(obj)); // this could race with the transition happening on the other thread + Monitor.Exit(obj); + } + } while (!t.Join(0)); + } + } } }