Skip to content
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

ObjectDisposedException when cancelling IHost.RunAsync() #36

Closed
bYt3w4Lk3r opened this issue Jul 29, 2021 · 5 comments
Closed

ObjectDisposedException when cancelling IHost.RunAsync() #36

bYt3w4Lk3r opened this issue Jul 29, 2021 · 5 comments
Assignees

Comments

@bYt3w4Lk3r
Copy link

Stack Trace:
System.ObjectDisposedException
HResult=0x80131622
Message=Cannot access a disposed object.
Object name: 'IServiceProvider'.
Source=Microsoft.Extensions.DependencyInjection
StackTrace:
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ThrowHelper.ThrowObjectDisposedException()
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider provider)
at Dapplo.Microsoft.Extensions.Hosting.Wpf.Internals.WpfThread.b__1_0(Object s, ExitEventArgs e) in D:\a\1\s\src\Dapplo.Microsoft.Extensions.Hosting.Wpf\Internals\WpfThread.cs:line 46
at System.Windows.Application.OnExit(ExitEventArgs e)
at System.Windows.Application.DoShutdown()
at System.Windows.Application.ShutdownImpl()
at System.Windows.Application.ShutdownCallback(Object arg)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
at System.Windows.Threading.DispatcherOperation.InvokeImpl()
at MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(Object obj)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at MS.Internal.CulturePreservingExecutionContext.Run(CulturePreservingExecutionContext executionContext, ContextCallback callback, Object state)
at System.Windows.Threading.DispatcherOperation.Invoke()
at System.Windows.Threading.Dispatcher.ProcessQueue()
at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
at System.Windows.Threading.Dispatcher.Run()
at System.Windows.Application.RunDispatcher(Object ignore)
at System.Windows.Application.RunInternal(Window window)
at Dapplo.Microsoft.Extensions.Hosting.Wpf.Internals.WpfThread.UiThreadStart() in D:\a\1\s\src\Dapplo.Microsoft.Extensions.Hosting.Wpf\Internals\WpfThread.cs:line 90
at Dapplo.Microsoft.Extensions.Hosting.UiThread.BaseUiThread`1.InternalUiThreadStart() in D:\a\1\s\src\Dapplo.Microsoft.Extensions.Hosting.UiThread\BaseUiThread.cs:line 68
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()

This exception was originally thrown at this call stack:
[External Code]

Code:
public static async Task Main(string[] _)
{
CancellationTokenSource cancellationToken = new(TimeSpan.FromSeconds(20));
await host.RunAsync(cancellationToken.Token);
}

@mgnslndh
Copy link
Contributor

I have encountered this exception as well. For me it occurred when running the WPF sample on net48 and pressing ctrl-c in the command prompt. Running the WPF sample as is and cancelling got me another exception instead.

@mgnslndh
Copy link
Contributor

I've made a "minimal" reproduction of this exception: https://github.com/mgnslndh/WpfDapplo/tree/master/ObjectDisposed/ObjectDisposed/ObjectDisposed

@mgnslndh
Copy link
Contributor

This is what I have found:

  1. When pressing ctrl-c from command prompt the WpfHostedService will be stopped and this in turn invokes Shutdown on the WPF application. At this point an injected IServiceProvider will not yet be disposed.
  2. The WpfThread listens to the WPF application Exit eventhandler and is called shortly after. At this point the IServiceProvider is disposed and it is not possible to get the IHostApplicationLifetime to stop the application.

Step two seems like a bug to me. If the WpfHostedService, which handles lifetime, is stopped it should not trigger yet another call to stop the host application, right?

Adding a conditional check before trying to stop the application will skip the call when the application is already stopping/stopped but make the call when you close the application from WPF.

var lifetime = ServiceProvider.GetService<IHostApplicationLifetime>();

// Register to the WPF application exit to stop the host application
wpfApplication.Exit += (s, e) =>
{
    UiContext.IsRunning = false;
    if (UiContext.IsLifetimeLinked)
    {
        if(!lifetime.ApplicationStopping.IsCancellationRequested && !lifetime.ApplicationStopped.IsCancellationRequested)
        {
            //_logger.LogDebug("Stopping host application due to WPF application exit.");
           lifetime.StopApplication();
        }
    }
};

However, this might be a quick and dirty solution. Might be better to cache the IHostApplicationLifetime in the UIContext?

Another solution could be to move the Exit event handler to the WpfHostedService where you could even remove the event handler before invoking Shutdown since you know you do not need it at the point.

@Lakritzator Lakritzator self-assigned this Mar 18, 2022
@Lakritzator
Copy link
Member

I had a bit of a look through the code, and the issue is that the IServiceProvider is scoped to the ConfigureServices and not guaranteed to be available later on. It's a bit documented in some issues, not very transparent. aspnet/Hosting#1106

I've stored the IHostApplicationLifetime in the BaseUiThread.
There is actually some code duplication, HandleApplicationExit should have been called.

Anyway, I will bring out a small fix for that on the current version, and afterwards update some more code & dependencies to make a 1.0 for now.

Just out of curiosity, can you tell me for what you are using this?

@Lakritzator
Copy link
Member

Fixed in 0.6.7
There is also a 1.0.3, which also has the fix, but it is a bit newer with updated dependencies.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants