locked
UseTaskFriendlySynchronizationContext causes problems don't know what it does

    Question

  • Page code:

    <asp:ListView ID="ProductList" runat="server">
        <LayoutTemplate>
            <asp:PlaceHolder ID="itemPlaceHolder" runat="server" />
            <asp:DataPager runat="server" QueryStringField="p" PageSize="10">
                <Fields>
                    <asp:NumericPagerField />
                </Fields>
            </asp:DataPager>
        </LayoutTemplate>
        <ItemTemplate>
            <%# Eval("Name") %><br />
        </ItemTemplate>
    </asp:ListView>


    protected async void Page_Load(object sender, EventArgs e)
    {
        ProductList.DataSource = await new webservice().GetProductsAsync();
        ProductList.DataBind();
    }

    If I set UseTaskFriendlySynchronizationContext in web.config to true I get an exception stating Request is not available which is triggered by the paging control. With paging control removed, ListView does not render even though the webservice correctly returns a collection of products. If I remove UseTaskFriendlySynchronizationContext from web.config then everything works fine, paging and ListView rendering. What I'm I doing wrong?

    I tried searching MSDN docs about this config setting but found nothing. I did post a comment on this post asking what it does and got a reply from Damian Edwards, a Microsoft employee, who stated "The new context is required when using the new Task based async features in ASP.NET in 4.5". For me asynchronization still works without that web.config setting using the new keywords and I can do things like chain async calls using Task's ContinueWith method etc and they all seem to work, so what new Task features is he referring to that require it?

    Saturday, March 10, 2012 11:20 PM

Answers

  • ASP.NET 4.5 Beta does not support async void methods on Page.  For now, use the RegisterAsyncTask API, as demonstrated below:

    protected void Page_Load(object sender, EventArgs e) {
        RegisterAsyncTask(new PageAsyncTask(DoSomethingAsync));
    }
    
    private async Task DoSomethingAsync() {
        ProductList.DataSource = await new webservice().GetProductsAsync();
        ProductList.DataBind();
    }
    You're not the only person who wants to be able to write async void methods, and we're considering ideas for how to make this work in the future.  But unfortunately we couldn't do anything for Beta.  The technical reason this doesn't work at the moment is that the ASP.NET Page type has only one asynchronous code execution point, both in v4.0 and in v4.5 - and that's the RegisterAsyncTask method.  To answer your question re: the <appSettings> switch, that tells ASP.NET to use an entirely new asynchronous pipeline which follows CLR conventions for kicking off asynchronous operations, including returning threads to the ThreadPool when necessary.  ASP.NET 4.0 and below followed its own conventions which went against CLR guidelines, and if the <appSettings> switch is not enabled it is *very* easy for asynchronous methods to run synchronously, deadlock the request, or otherwise not behave as expected.  In your particular example, having an async void method without setting the <appSettings> switch actually introduces a race condition in your Page, where the DataBind() method is not guaranteed to be called before the Page is actually rendered and its output sent to the client.
    Monday, March 12, 2012 2:17 AM

All replies

  • ASP.NET 4.5 Beta does not support async void methods on Page.  For now, use the RegisterAsyncTask API, as demonstrated below:

    protected void Page_Load(object sender, EventArgs e) {
        RegisterAsyncTask(new PageAsyncTask(DoSomethingAsync));
    }
    
    private async Task DoSomethingAsync() {
        ProductList.DataSource = await new webservice().GetProductsAsync();
        ProductList.DataBind();
    }
    You're not the only person who wants to be able to write async void methods, and we're considering ideas for how to make this work in the future.  But unfortunately we couldn't do anything for Beta.  The technical reason this doesn't work at the moment is that the ASP.NET Page type has only one asynchronous code execution point, both in v4.0 and in v4.5 - and that's the RegisterAsyncTask method.  To answer your question re: the <appSettings> switch, that tells ASP.NET to use an entirely new asynchronous pipeline which follows CLR conventions for kicking off asynchronous operations, including returning threads to the ThreadPool when necessary.  ASP.NET 4.0 and below followed its own conventions which went against CLR guidelines, and if the <appSettings> switch is not enabled it is *very* easy for asynchronous methods to run synchronously, deadlock the request, or otherwise not behave as expected.  In your particular example, having an async void method without setting the <appSettings> switch actually introduces a race condition in your Page, where the DataBind() method is not guaranteed to be called before the Page is actually rendered and its output sent to the client.
    Monday, March 12, 2012 2:17 AM
  • That answers all my questions, thanks a lot.
    Monday, March 12, 2012 4:22 AM
  • ASP.NET 4.0 and below followed its own conventions which went against CLR guidelines, and if the <appSettings> switch is not enabled it is *very* easy for asynchronous methods to run synchronously, deadlock the request, or otherwise not behave as expected.

    I don't understand why async void doesn't work today, unless the AsyncVoidMethodBuilder semantics have changed since the CTP. In the CTP, AsyncVoidMethodBuilder registers its operation with the SynchronizationContext, so ASP.NET knows the request isn't complete until all async void methods have completed.

           -Steve


    Programming blog: http://nitoprograms.blogspot.com/
      Including my TCP/IP .NET Sockets FAQ
      and How to Implement IDisposable and Finalizers: 3 Easy Rules
    Microsoft Certified Professional Developer

    How to get to Heaven according to the Bible

    Monday, March 12, 2012 1:29 PM
  • In the CTP, AsyncVoidMethodBuilder registers its operation with the SynchronizationContext, so ASP.NET knows the request isn't complete until all async void methods have completed.

    The ASP.NET runtime knows this, but the Page class doesn't know this.  From the perspective of Page.ProcessRequest, the Page_Load method returned, so it just immediately moves on to ProcessPostData, RaiseChangedEvents, and other Page pipeline methods.  The Page type doesn't query the SynchronizationContext between these Page pipeline methods.  (After all, why would it?  Remember - Page has existed since the late 90s, and when it was designed nobody ever imagined that somebody might write an asynchronous Page_Load method.)

    Monday, March 12, 2012 4:23 PM
  • Is this still true if Page.Async is set to true?

           -Steve


    Programming blog: http://nitoprograms.blogspot.com/
      Including my TCP/IP .NET Sockets FAQ
      and How to Implement IDisposable and Finalizers: 3 Easy Rules
    Microsoft Certified Professional Developer

    How to get to Heaven according to the Bible

    Monday, March 12, 2012 4:31 PM
  • Is this still true if Page.Async is set to true?

    Yes.  We realize that this is painful behavior, so we're trying to find ways to make the Page class itself aware of the fact that a developer may have kicked off asynchronous work, and if so to pause page processing (without blocking a thread!) until that work is complete.  That way, we don't just immediately move on to ProcessPostData, etc.

    Monday, March 12, 2012 5:17 PM