runtime: BackgroundService appears to create tasks that will never complete
This line appears to create a task that will most likely never complete: https://github.com/aspnet/Extensions/blob/aa7fa91cfc8f6ff078b020a428bcad71ae7a32ab/src/Hosting/Abstractions/src/BackgroundService.cs#L65
Based on my own understanding of BackgroundService and the description of its use of cancellation in dotnet/extensions#1245, I don’t expect the CancellationToken to StartAsync or StopAsync to be marked as cancelled under normal operation. (This is why BackgroundService exists: it provides a more useful CancellationToken to ExecuteAsync than the one provided to StartAsync, which is helpful for tasks that run indefinitely.) That means the task created with this expression is likely to never complete:
Task.Delay(Timeout.Infinite, cancellationToken)
To resolve this, a new CancellationTokenSource could be created for the current scope:
using var stopCts = new CancellationTokenSource();
using var _ = cancellationToken.Register(stopCts.Cancel);
await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite, stopCts.Token));
I can submit a PR if this fix makes sense.
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Comments: 28 (26 by maintainers)
FYI: We aren’t accepting new PRs for 3.0 right now and there isn’t a branch for 3.1 (the next release after 3.0) yet. I would expect one to be up and running sometime next week though. Just want to set the expectations. We’re starting to lock down for 3.0’s release later in September.
I think a PR would be OK (though would want input from @Tratcher and @davidfowl). I think that using a separate “stop the timeout” token would be preferred rather than relying on the fact that chaining the tokens like you did above means that when the method ends the timeout will cancel (because the CTS is disposed).
@moonrockfamily I blogged about that recently - part of a 3-part blog series. I use all three techniques to create a “critical
BackgroundService
” base type that avoids startup delay (Task.Run
), catchesExecuteAsync
exceptions, and logs and then exits the host if a critical background service fails.I use
WaitAsync(CancellationToken)
, in an attempt to make it clear that it is the wait that is cancelled and not the task.I think the solutions in your extension method are sufficient for our code, you can create a PR with that. I agree that the stuff in Microsoft.VisualStudio.Threading is a little more complicated than we need here (it does a lot of work to avoid allocating extra async state machines).
Like this? https://github.com/dotnet/corefx/issues/33007 😃
Fair point.
Sounds good. Probably the best option since it’s clear as to what’s happening.