Closed jasonswearingen closed 2 years ago
using await autoResetEvent.WaitAsync();
instead of the TimSpan
overload does not seem to deadlock
@jasonswearingen , thanks for the report! Regardless of potential bug, I'm trying to figure out what happening in your test. I would appreciate if you can help me with that. I see the following:
I see that Producer thread has no async code (no await operators). Moreover, the test waits for Producer completion only. So I'm not sure how AsyncAutoResetEvent
can block the entire test. Even if it is bugged, it's not observable by the test. I think, you miss await consumerTask
.
yes you are correct regarding the test workflow.
And yes, there is a small bug in the test that would leave the consumer task stalled if the deadlock did not exist. The last line should have:
await producerTask;
autoResetEvent.Set();
I have updated the test code accordingly.
Also, there is an interesting bug in .NET itself that was fixed: dotnet/runtime#60182. It is observable for very small timeouts only. 1 milliseconds is enough to blow up on that mine. Unfortunately, the fix is not included in .NET 6 RC2. Timeout tracking in all asynchronous primitives rely on CancellationTokenSource
and its ability to reset the state. The bug unpredictably crashes the internal state of DotNext.Threading.Tasks.ValueTaskCompletionSource<T>
that might be a source of deadlock.
@jasonswearingen , maybe
await producerTask;
autoResetEvent.Set();
await consumerTask;
?
sure that would be better :)
btw when I ran it, the producer thread is hung inside the .Set()
call, and further up the callstack it is stuck inside the ValueTaskCompletionSource
. So your theory seems valid.
Unfortunately, we have to wait for final release of .NET 6 and then check again.
@jasonswearingen , .NEXT RC1 has been released. It uses GA version of .NET 6 so you can try tests again.
The bug is still there. deadlocks in same location. tested with dotnet6.0 release and dotNext 4.0.0-rc.1
I found the root cause:
ManualResetCompletionSource.CancellationRequested
is trying to enter monitor infinitelyManualResetCompletionSource.OnCompleted
is trying to enter monitor infinitelyManualResetCompletionSource.StopTrackingCancellation
stuck on timeoutTracker.Dispose()
methodtimeoutTracker
is of type CancellationTokenRegistration
. I think that Dispose
waits for completion of cancellation callback. In the same time, cancellation callback stuck on Thread # 1. So Dispose
is called within monitor and cancellation callback too. As a result, we observe a dead lock.
According to sources in .NET repo, Dispose
call is blocking while Unregister
not.
@jasonswearingen , many thanks for reporting this bug! The potential fix is in develop
branch. The related commit is specified above.
Run this test. On my computer, it deadlocks after about 6 seconds
using DotNext 4.0.0-beta.8 from Nuget