7 Comments

  1. Robert N.

    Hi Robert,

    thank you for the insight – saved my life. It works, but I don’t understand one thing:
    Why do you call “ConfigureAwait(false)”? I figured if I don’t call that, the ObjectDisposedException will not be caught.
    Would appreciate it if you had the time to clarify this.

    Thank you,
    Robert.

    • Hey Robert,

      You don’t need to use ConfigureAwait(false), it will behave the same either way.

      The ConfigureAwait(false) makes it so that the Context isn’t saved for when the thread returns. In the case of a MVC controller, for example, where you have an HttpContext, you wouldn’t want to put ConfigureAwait(false) because you want that HttpContext to still be the same one when the awaited call returns. In other scenarios (like this one) where there is no context, putting ConfigureAwait(false) saves a CPU cycle or two. Stephen Cleary goes in to it in a lot more detail on his blog.

      Hope this helps!

      • Robert N.

        Thank you very much for the fast reply!

        I am still trying to wrap my head around that blog
        post.

        My problem is that the behaviour is not the same here if I omit the ConfigureAwait(false).

        This is the call:
        var tcpClient = await m_TcpListener
        .AcceptTcpClientAsync()
        .ConfigureAwait(false);

        It is wrapped in a “try-catch(ObjectDisposedException)” handler.
        Without the ConfigureAwaits(false) the exception is never caught, allthough it is still raised.

        With and without ConfigureAwaits(false) the debugger tells me that the exception is raised somewhere in “System.Net.Sockets.dll” and my program is not even on the call stack. After continuing the breakpoint in the try-catch-handler is hit with that same exception, but only if ConfigureAwaits(false) was used.

        Possibly the exception is not handed over to the thread on which await was used? I should probably not use “await” without understanding what is going on here.

        Best,
        Robert.

        • Hey Robert,

          The ObjectDisposedException does indeed originate from System.Net.Sockets, so that seems right to me.

          I wrote this little app that cancels when you press “escape”, and its all working as expected for me with and without the ConfigureAwait(false)… Anything look different compared to your code?

  2. Robert N.

    Excellent, this is working for me and it proved it had to be something specific about my program. So I started digging:

    My code is essentially the same as your but its a WPF app (.NET Core 3.1).

    I created a console app (.NET Core 3.1) and moved the listening stuff there -> the problem was gone.

    I created a WPF app (.NET Core 3.1) and copied the listening stuff there -> the problem was gone.

    Comparing to my main app there was only one difference left and it proved to be the cause of all this:
    – My test WPF app cancelled from the OnClosing event handler of the main window.
    – My main app cancelled from the OnExit overload of the App class

    If you cancel the token from the OnExit overload of the Application class the catch handle will only work if the await was configured with ConfigureAwait(false).

    Here is the App class (main window implementation does nothing). If you place a breakpoint in the catch handler and alternate between running this with/without ConfigureAwaits, the described behaviour should be observable:

    public partial class App : Application
    {
    #region Private Fields

    TcpListener m_TcpListener;
    bool m_Listening;
    CancellationTokenSource m_TcpListenerAcceptTokenSource;

    #endregion

    public void Stop()
    {
    m_Listening = false;
    m_TcpListenerAcceptTokenSource.Cancel();
    }

    #region Protected Overriding Application

    protected override void OnStartup(StartupEventArgs e)
    {
    m_TcpListener = new TcpListener(IPAddress.Any, 12366);

    // Must be set before calling start
    m_Listening = true;
    m_TcpListener.Start();

    AcceptClients();

    base.OnStartup(e);
    }

    protected override void OnExit(ExitEventArgs e)
    {
    Stop();

    base.OnExit(e);
    }

    #endregion

    #region Private Methods

    async void AcceptClients()
    {
    while (m_Listening)
    {
    if (m_TcpListenerAcceptTokenSource == null)
    m_TcpListenerAcceptTokenSource = new CancellationTokenSource();

    using (m_TcpListenerAcceptTokenSource.Token.Register(m_TcpListener.Stop))
    {
    try
    {
    // The ConfigureAwait must be called if we want to be
    // able to catch the ObjectDisposedException caused by
    // calling Stop() while a AcceptTcpClientAsync() is pending
    // Many details on ConfigureAwait:
    // https://devblogs.microsoft.com/dotnet/configureawait-faq/
    var tcpClient = await m_TcpListener
    .AcceptTcpClientAsync()
    .ConfigureAwait(false);
    }
    catch (ObjectDisposedException ex) when (m_TcpListenerAcceptTokenSource.IsCancellationRequested)
    {
    m_TcpListenerAcceptTokenSource = null;
    }
    }
    }
    }

    #endregion
    }

    • Robert N.

      PS: The blog post I linked in the comment inside the source code above did not help me at that point, it rather convinced me that I don’t really understand what is going. Before you helped me find the cause of the problem I already implemented a solution without async/await and with a Thread instance instead, because I try to never use code I don’t understand. Its very cool we found the cause! Still don’t know the exact reason – something special about the program state when the app is being shutdown?

      • Glad it is working for you now, but it does seem kind of strange… the UI thread on a WPF app (like the one that would execute OnStartup) has a Context object that you would want to maintain if you’re doing a longer-running call. The default configuration of ConfigureAwait(true) would make it so that this context is preserved, allowing you to have access to the Context object again once control is yielded back to the thread that called the await. However, looking at your code, it doesn’t seem like you would probably need access to the Context, especially since it is when the app is closing.

        I’m wondering if the AcceptClients(); is the problem. You can see some weird stuff with async void functions, and I think its generally advised to avoid them if possible. You could try launching it as a separate thread via Task.Run and changing it to async Task… something like:

        Task.Run(AcceptClients);

        private async Task AcceptClients() { … }

        In that way, you might not need to use the ConfigureAwait(false).

Leave a Reply

Your email address will not be published. Required fields are marked *