-
Notifications
You must be signed in to change notification settings - Fork 4.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
BackgroundService.StopAsync never completes when called from a WPF project from Application.Exit event or Application.OnExit override #42993
Comments
I found myself in the same situation. My use is pretty much the same: public partial class App : Application
{
private IHost _host;
public App()
{
_host = CreateHostBuilder().Build();
}
private async void Application_Startup(object sender, StartupEventArgs e)
{
await _host.StartAsync();
_host.Services.GetService<MainWindow>().Show();
}
private async void Application_Exit(object sender, ExitEventArgs e)
{
using (_host)
{
_host.Services.GetService<MainWindow>().Close();
await _host.StopAsync(TimeSpan.FromSeconds(5));
}
}
} After calling
Maybe there is a "proper way" of using Microsoft.Extensions and IHost with WPF but I have no idea how to make them both work properly together. When putting the Any insights on how to make Microsoft.Extensions and WPF work together nicely? |
I got some help in the C# Discord, here's a workaround: private async void Application_Exit(object sender, ExitEventArgs e)
{
_host.Services.GetService<MainWindow>().Close();
var stopTask = _host.StopAsync(TimeSpan.FromSeconds(5));
_host.Dispose();
await stopTask;
} For some reason, the StopAsync never actually exits. So, while not a good solution, it works. Still wondering what the proper way would be. |
Hi, thanks for the workaround. While the workaround works most of the times, it sometimes causes an |
Yeah it's not a good solution, but can't really find any help with how to approach this situation. As for the exception, a good try/catch and that would do it haha. |
This looks similar to #35990 Another workaround is to register a different |
I've tried to implement EDIT: Updated sample project with all the relevant parts (IHostLifetime and IHostedService implementations) Here's my attempt: public class WPFAppLifetime : IHostLifetime, IDisposable
{
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public Task WaitForStartAsync(CancellationToken cancellationToken)
{
AppDomain.CurrentDomain.ProcessExit += OnProcessExit;
return Task.CompletedTask;
}
private void OnProcessExit(object sender, EventArgs e)
{
Environment.ExitCode = 0;
}
public void Dispose()
{
AppDomain.CurrentDomain.ProcessExit -= OnProcessExit;
}
} public static class WPFLifetimeHostExtensions
{
public static IHostBuilder UseWPFAppLifetime(this IHostBuilder hostBuilder)
{
return hostBuilder.ConfigureServices((hostContext, services) => services.AddSingleton<IHostLifetime, WPFAppLifetime>());
}
} _host = CreateHostBuilder().ConfigureServices((context, services) =>
{
services.AddHostedService<MyService>();
services.AddSingleton<MainWindow>();
}).UseWPFAppLifetime().Build(); |
Tagging subscribers to this area: @eerhardt, @maryamariyan |
Try this: private void OnApplicationExit(object sender, ExitEventArgs e) // this method cannot be async, it MUST wait!
{
Task.Run(() => _host.StopAsync()).Wait();
_host.Dispose();
_host = null;
} Or something like this: private void OnApplicationExit(object sender, ExitEventArgs e) // this method cannot be async, it MUST wait!
{
var frame = new DispatcherFrame(false);
Task.Run(() => {
try {
await _host.StopAsync();
}
finally {
frame.Continue = false;
}
});
Dispatcher.PushFrame(frame);
_host.Dispose();
_host = null;
} |
Test results, suggested solution 1: Test results, suggested solution 2: I leave to owners decision to close issue or not. |
With #56057 we no longer block on ProcessExit in the ConsoleLifetime on net6.0. So this issue is now fixed. However, the correct fix here is to not use ConsoleLifetime in a WPF app, since it is not a "console app". So implementing your own WpfLifetime is the right idea. |
Thanks @eerhardt, but I should clarify the issue here was not the ConsoleLifetime (in fact implementing WpfAppLifetime was a test I've commented above) but the usage of Tasks from the OnApplicationExit event. |
Description
BackgroundService StopAsync never completes when called from a WPF project from Application.Exit event or Application.OnExit override. Not sure this is the right repo to open the issue so be patient with me.
To Reproduce
Steps to reproduce the behavior (sample project)
Relevant parts:
Expected behavior
The StopAsync should complete or timeout.
Additional context
I think a deadlock occurs but I've no idea how to prevent it. I've tried to debug the BackgroundService class and the offending part is the
Task.WhenAny
included inStopAsync
. I've also tried to wrap the call toStopAsync
in aTask
but the effect is the same.Any suggestion is appreciated.
The text was updated successfully, but these errors were encountered: