Fix ScheduledTaskExecutor deadlock when TrySetResult runs continuations inline#2953
Conversation
ScheduledTaskExecutor deadlock when TrySetResult runs continuations inline
@dotnet-policy-service agree company="Redgate Software Ltd" |
test/Polly.Core.Tests/CircuitBreaker/Controller/ScheduledTaskExecutorTests.cs
Outdated
Show resolved
Hide resolved
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2953 +/- ##
=======================================
Coverage 96.15% 96.15%
=======================================
Files 309 309
Lines 7128 7128
Branches 1005 1005
=======================================
Hits 6854 6854
Misses 221 221
Partials 53 53
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
|
@crnhrv In the process of releasing this change, one of the new tests failed here: Does this indicate an issue with the fix, or is it just that the timeout is too small for CI? |
|
@martincostello Hmm, the tests were passing on my machine in under <10ms so I thought 250ms would be safe enough for CI, but I think that's the only explanation considering the actual behaviour should be deterministic otherwise as far as I can tell. I notice it failed on tests targeting |
|
Thanks for your contribution @crnhrv - the changes from this pull request have been published as part of version 8.6.6 📦, which is now available from NuGet.org 🚀 |
|
There was also a failure here for net10.0: logs If you're happy that it's just a test flake, then I'll do a PR to increase the value. |
|
I've been running the tests until failure locally in ~10 concurrent sessions for the past 5 minutes or so and haven't hit a failure so I can't imagine it's flaky for any other reason than the timeout being too low. |
Pull Request
The issue or feature being addressed
Fixes #2948 —
ScheduledTaskExecutordeadlock whenTrySetResultruns continuations inline.Details on the issue fix or feature implementation
ScheduledTaskExecutor.ScheduleTaskwas creating itsTaskCompletionSource<object>withoutTaskCreationOptions.RunContinuationsAsynchronously. This meant that whenStartProcessingAsynccalledTrySetResult, any continuation awaiting the returned task could run inline on the executor's single processing thread. If that continuation blocked (e.g. by holding a lock and making a synchronous Polly call, or by waiting for a second scheduled task), the executor thread would stall and deadlock.To fix we pass
TaskCreationOptions.RunContinuationsAsynchronouslyto theTaskCompletionSourceconstructor, ensuring continuations are always sent to the thread pool rather than running inline.A regression test is included that forces a continuation to run synchronously via
TaskContinuationOptions.ExecuteSynchronouslyand has it block waiting for a second scheduled task. This deadlocks (and is cancelled after 250ms) without the fix.Confirm the following