Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Disposing Microsoft.Owin.Hosting.WebApp throws 'System.ObjectDisposedException'

We have a self-hosted SignalR server in our WPF application. The WebApp gets started on application startup. On application exit we dispose of the WebApp.

    public void Start()
    {
        myWebApp = WebApp.Start<MyApp>(url);
    }

    private void Dispose(bool isDisposing)
    {
        if (disposed) return;

        if (isDisposing)
            myWebApp.Dispose();

        disposed = true;
    }

The call to myWebApp.Dispose() raises a 'System.ObjectDisposedException'. Am I doing something wrong? The Microsoft.Owin.* dlls have the version 2.1.0 and the SignalR self host 2.0.3

UPDATE: Turns out this is the first chance exception which I can see in visual studio because the setting "break on clr exceptions" is active. This exception seems to be handled internally and does not bubble up into our code

like image 427
Helikaon Avatar asked Apr 10 '14 08:04

Helikaon


1 Answers

After exploring the Katana source code, I found a reason for this problem. It is the Microsoft.Owin.Host.HttpListener.OwinHttpListener.ProcessRequestsAsync() method. It starts while-loop containing _listener.GetContextAsync() call of a private HttpListener instance in try-catch section.

Also class implements IDisposable and contains a Dispose() method. This method disposes the private HttpListener instance.

When you call WebApp.Start() it returns an instance of IDisposable, that only has Dispose() method, that disposes OwinHttpListener.

So, when you dispose it, you call its Dispose() method of OwinHttpListener, which disposes the private HttpListener.

But at the same time ProcessRequestsAsync() calls _listener.GetContextAsync(), but _listener is already disposed and throws ObjectDisposedException. catch block log the exception and returns from ProcessRequestsAsync().

I think, that double check lock in ProcessRequestsAsync() may be a good option.

private async void ProcessRequestsAsync()
{
    while (_listener.IsListening && CanAcceptMoreRequests)
    {
        Interlocked.Increment(ref _currentOutstandingAccepts);
        HttpListenerContext context;
        try
        {
            context = await _listener.GetContextAsync();
        }
        (SOME_OTHER_CATCHES)
        catch (ObjectDisposedException ode)
        {
            // These happen if HttpListener has been disposed
            Interlocked.Decrement(ref _currentOutstandingAccepts);
            LogHelper.LogException(_logger, "Accept", ode);
            return;
        }
        (SOME_OTHER_CODE)
    }
}

public void Dispose()
{
    if (_listener.IsListening)
    {
        _listener.Stop();
    }

    ((IDisposable)_listener).Dispose();
}
like image 195
Tim Avatar answered Sep 19 '22 23:09

Tim