Repository navigation
Closed
Labels
area-System.Threadingdisabled-testThe test is disabled in source code against the issueThe test is disabled in source code against the issueruntime-monospecific to the Mono runtimespecific to the Mono runtime
Milestone
Description
There's a current bug that can happen when using synchronization contexts that allow message pumping:
- The main thread calls Monitor.Wait on lock A.
- SynchronizationContext.Wait pumps a message and the main thread calls Monitor.Wait on lock B.
- A background thread calls Monitor.Pulse on lock A but lock B steals the signal.
runtime/src/libraries/System.Threading/tests/MonitorTests.cs
Lines 518 to 560 in d343974
| public static void ReentrantWaitFromSyncContextTest() | |
| { | |
| // Since we set the SynchronizationContext for the current thread, we run | |
| // this test in a background thread to avoid affecting other tests. | |
| ThreadTestHelpers.RunTestInBackgroundThread(() => | |
| { | |
| object lockA = new(); | |
| object lockB = new(); | |
| bool innerWaitResult = true; // should become false (lockB is never pulsed) | |
| var pumpCtx = new ReentrantWaitSyncContext(() => | |
| { | |
| lock (lockB) | |
| { | |
| innerWaitResult = Monitor.Wait(lockB, 500); | |
| } | |
| }); | |
| SynchronizationContext.SetSynchronizationContext(pumpCtx); | |
| var t = new Thread(() => | |
| { | |
| Thread.Sleep(100); | |
| lock (lockA) { Monitor.Pulse(lockA); } | |
| }) { IsBackground = true }; | |
| t.Start(); | |
| bool outerResult; | |
| var sw = System.Diagnostics.Stopwatch.StartNew(); | |
| lock (lockA) | |
| { | |
| outerResult = Monitor.Wait(lockA, FailTimeoutMilliseconds); | |
| } | |
| long elapsed = sw.ElapsedMilliseconds; | |
| Assert.True(outerResult, "Outer Monitor.Wait should have been pulsed"); | |
| Assert.True(elapsed < FailTimeoutMilliseconds / 2, | |
| $"Outer Monitor.Wait took {elapsed}ms — signal was likely stolen by inner wait"); | |
| Assert.False(innerWaitResult, | |
| "Inner Monitor.Wait(lockB) should return false (lockB was never pulsed)"); | |
| t.Join(FailTimeoutMilliseconds); | |
| }); | |
| } |
Metadata
Metadata
Assignees
Labels
area-System.Threadingdisabled-testThe test is disabled in source code against the issueThe test is disabled in source code against the issueruntime-monospecific to the Mono runtimespecific to the Mono runtime
Activity
dotnet-policy-service commentedon Mar 26, 2026
Tagging subscribers to this area: @agocke, @VSadov
See info in area-owners.md if you want to be subscribed.
JulieLeeMSFT commentedon Jul 27, 2026
@eduardo-vp, do you plan to fix this issue in .NET 11?
cc @VSadov.
eduardo-vp commentedon Jul 29, 2026
I looked more into this and actually the initial issue #123173 was possible because after Monitor was moved to managed code in #118371, Monitor.Wait now blocks on an AutoResetEvent via
WaitOneNoCheckwhich ends up forwarding the wait toSynchronizationContext.Waitwhen the context has calledSetWaitNotificationRequired. On Mono Monitor.Wait is still native, doesn't go through that override so there's no room for the bug to happen.This is more of a parity gap, Mono doesn't honor SynchronizationContext wait notification which matches CoreCLR in .NET 10 and earlier. Not sure if this is something we actually need to address but for now I think the test just shouldn't run on Mono since it lacks the behavior necessary to repro the bug.
JulieLeeMSFT commentedon Aug 24, 2026
Set to .NET 12.
Disable ReentrantWaitFromSyncContextTest test on Mono (#131548)