Search code examples
c#.netasp.nettask-parallel-libraryasync-ctp

Async CTP Bug - Task Never Completes


Firstly a forward apology: I cannot isolate the following bug into a simple console application. However, in my relatively simple ASP.NET Web Forms application, the following code will cause the current thread to block indefinitely:

public class MyModule : IHttpModule
{
    public void Dispose()
    {
    }

    public void Init(System.Web.HttpApplication context)
    {
        context.BeginRequest += this.Context_BeginRequest;
    }

    private void Context_BeginRequest(object sender, EventArgs e)
    {
        Sleep().Wait();
        var x = 2; // This line is never hit.
    }

    private async Task Sleep()
    {
        await TaskEx.Run(() => System.Threading.Thread.Sleep(1000));
    }
}

The task state just remains as 'WaitingForActivation'. Does anyone know why this would happen?


Solution

  • EDIT: The comment from Stephen Cleary sheds more light:

    AspNetSynchronizationContext is the strangest implementation. It treats Post as synchronous rather than asynchronous and uses a lock to execute its delegates one at a time. AspNetSynchronizationContext does not require marshaling back to the same thread (but does need to take the lock); the deadlock on Wait is because the continuation is waiting for the lock (held by the thread in the event handler)


    My guess is that there's a SynchronizationContext which is forcing the continuation to run on the same thread as the event handler. Your event handler is blocking that thread, so the continuation never runs, which means the event handler will never unblock.

    That's only a guess though - it's the only thing I can think of which makes sense at the moment.

    One option to try to unblock this is to change your Sleep method to:

    private async Task Sleep()
    {
        await TaskEx.Run(() => System.Threading.Thread.Sleep(1000))
                    .ConfigureAwait(continueOnCapturedContext: false);
    }
    

    That will allow the continuation to complete on a different context.

    I'm surprised there is such a synchronization context, mind you... I'd expect all of this to just happen on the thread pool. Possibly BeginRequest is treated slightly specially.