Await, and UI, and deadlocks! Oh my!

Await, and UI, and deadlocks! Oh my!

Rate This
  • Comments 13

It’s been awesome seeing the level of interest developers have had for the Async CTP and how much usage it’s getting.  Of course, with any new technology there are bound to be some hiccups.  One issue I’ve seen arise now multiple times is developers accidentally deadlocking their application by blocking their UI thread, so I thought it would be worthwhile to take a few moments to explore the common cause of this and how to avoid such predicaments.

At its core, the new async language functionality aims to restore the ability for developers to write the sequential, imperative code they’re used to writing, but to have it be asynchronous in nature rather than synchronous.  That means that when operations would otherwise tie up the current thread of execution, they’re instead offloaded elsewhere, allowing the current thread to make forward progress and do other useful work while, in effect, asynchronously waiting for the spawned operation to complete.  In both server and client applications, this can be crucial for application scalability, and in client applications in particular it’s also really useful for responsiveness.

Most UI frameworks, such as Windows Forms and WPF, utilize a message loop to receive and process incoming messages.  These messages include things like notifications of keys being typed on a keyboard, or buttons being clicked on a mouse, or controls in the user interface being manipulated, or the need to refresh an area of the window, or even the application sending itself a message dictating some code to be executed.  In response to these messages, the UI performs some action, such as redrawing its surface, or changing the text being displayed, or adding items to one of its controls., or running the code that was posted to it.  The “message loop” is typically literally a loop in code, where a thread continually waits for the next message to arrive, processes it, goes back to get the next message, processes it, and so on.  As long as that thread is able to quickly process messages as soon as they arrive, the application remains responsive, and the application’s users remain happy.  If, however, processing a particular message takes too long, the thread running the message loop code will be unable to pick up the next message in a timely fashion, and responsiveness will decrease.  This could take the form of pauses in responding to user input, and if the thread’s delays get bad enough (e.g. an infinite delay), the application “hanging”.

In a framework like Windows Forms or WPF, when a user clicks a button, that typically ends up sending a message to the message loop, which translates the message into a call to a handler of some kind, such as a method on the class representing the user interface, e.g.:

private void button1_Click(object sender, RoutedEventArgs e)
{
    string s = LoadString();
    textBox1.Text = s;
}

Here, when I click the button1 control, the message will inform WPF to invoke the button1_Click method, which will in turn run a method LoadString to get a string value, and store that string value into the textBox1 control’s Text property.  As long as LoadString is quick to execute, all is well, but the longer LoadString takes, the more time the UI thread is delayed inside button1_Click, unable to return to the message loop to pick up and process the next message.

To address that, we can choose to load the string asynchronously, meaning that rather than blocking the thread calling button1_Click from returning to the message loop until the string loading has completed, we’ll instead just have that thread launch the loading operation and then go back to the message loop.  Only when the loading operation completes will we then send another message to the message loop to say “hey, that loading operation you previously started is done, and you can pick up where you left off and continue executing.”  Imagine we had a method:

public Task<string> LoadStringAsync();

This method will return very quickly to its caller, handing back a .NET Task<string> object that represents the future completion of the asynchronous operation and its future result.  At some point in the future when the operation completes, the task object will be able to hand out the operations’ result, which could be the string in the case of successful loading, or an exception in the case of failure.  Either way, the task object provides several mechanisms to notify the holder of the object that the loading operation has completed.  One way is to synchronously block waiting for the task to complete, and that can be accomplished by calling the task’s Wait method, or by accessing its Result, which will implicitly wait until the operation has completed… in both of these cases, a call to these members will not complete until the operation has completed.  An alternative way is to receive an asynchronous callback, where you register with the task a delegate that will be invoked when the task completes.  That can be accomplished using one of the Task’s ContinueWith methods.  With ContinueWith, we can now rewrite our previous button1_Click method to not block the UI thread while we’re asynchronously waiting for the loading operation to complete:

private void button1_Click(object sender, RoutedEventArgs e)
{
    Task<string> s = LoadStringAsync();
    s.ContinueWith(delegate { textBox1.Text = s.Result; }); // warning: buggy
}

This does in fact asynchronously launch the loading operation, and then asynchronously run the code to store the result into the UI when the operation completes.  However, we now have a new problem.  UI frameworks like Windows Forms, WPF, and Silverlight all place a restriction on which threads are able to access UI controls, namely that the control can only be accessed from the thread that created it.  Here, however, we’re running the callback to update the Text of textBox1on some arbitrary thread, wherever the Task Parallel Library (TPL) implementation of ContinueWith happened to put it.  To address this, we need some way to get back to the UI thread.  Different UI frameworks provide different mechanisms for doing this, but in .NET they all take basically the same shape, a BeginInvoke method you can use to pass some code as a message to the UI thread to be processed:

private void button1_Click(object sender, RoutedEventArgs e)
{
    Task<string> s = LoadStringAsync();
    s.ContinueWith(delegate
    {
        Dispatcher.BeginInvoke(new Action(delegate
        {
            textBox1.Text = s.Result;
        }));
    });
}

The .NET Framework further abstracts over these mechanisms for getting back to the UI thread, and in general a mechanism for posting some code to a particular context, through the SynchronizationContext class.  A framework can establish a current context, available through the SynchronizationContext.Current property, which provides a SynchronizationContext instance representing the current environment.  This instance’s Post method will marshal a delegate back to this environment to be invoked: in a WPF app, that means bringing you back to the dispatcher, or UI thread, you were previously on.  So, we can rewrite the previous code as follows:

private void button1_Click(object sender, RoutedEventArgs e)
{
    var sc = SynchronizationContext.Current;
    Task<string> s = LoadStringAsync();
    s.ContinueWith(delegate
    {
        sc.Post(delegate { textBox1.Text = s.Result; }, null);
    });
}

and in fact this pattern is so common, TPL in .NET 4 provides the TaskScheduler.FromCurrentSynchronizationContext() method, which allows you to do the same thing with code like:

private void button1_Click(object sender, RoutedEventArgs e)
{
    LoadStringAsync().ContinueWith(s => textBox1.Text = s.Result,
        TaskScheduler.FromCurrentSynchronizationContext());
}

As mentioned, this works by “posting” the delegate back to the UI thread to be executed.  That posting is a message like any other, and it requires the UI thread to go through its message loop, pick up the message, and process it (which will result in invoking the posted delegate).  In order for the delegate to be invoked then, the thread first needs to return to the message loop, which means it must leave the button1_Click method.

Now, there’s still a fair amount of boilerplate code to write above, and it gets orders of magnitude worse when you start introducing more complicated flow control constructs, like conditionals and loops.  To address this, the new async language feature allows you to write this same code as:

private async void button1_Click(object sender, RoutedEventArgs e)
{
    string s = await LoadStringAsync();
    textBox1.Text = s;
}

For all intents and purposes, this is the same as the previous code shown, and you can see how much cleaner it is… in fact, it’s close to identical  in the code required to our original synchronous implementation.  But, of course, this one is asynchronous: after calling LoadStringAsync and getting back the Task<string> object, the remainder of the function is hooked up as a callback that will be posted to the current SynchronizationContext in order to continue execution on the right thread when the loading is complete.  The compiler is layering on some really helpful syntactic sugar here.

Now things get interesting. Let’s imagine LoadStringAsync is implemented as follows:

static async Task<string> LoadStringAsync()
{
    string firstName = await GetFirstNameAsync();
    string lastName = await GetLastNameAsync();
    return firstName + " " + lastName;
}

LoadStringAsync is implemented to first asynchronously retrieve a first name, then asynchronously retrieve a last name, and then return the concatenation of the two.  Notice that it’s using “await”, which, as pointed out previously, is similar to the aforementioned TPL code that uses a continuation to post back to the synchronization context that was current when the await was issued.  So, here’s the crucial point: for LoadStringAsync to complete (i.e. for it to have loaded all of its data and returned its concatenated string, completing the task it returned with that concatenated result), the delegates it posted to the UI thread must have completed.  If the UI thread is unable to get back to the message loop to process messages, it will be unable to pick up the posted delegates that resulted from the asynchronous operations in LoadStringAsync completing, which means the remainder of LoadStringAsync will not run, which means the Task<string> returned from LoadStringAsync will not complete.  It won’t complete until the relevant messages are processed by the message loop.

With that in mind, consider this (faulty) reimplementation of button1_Click:

private void button1_Click(object sender, RoutedEventArgs e)
{
    Task<string> s = LoadStringAsync();
    textBox1.Text = s.Result; // warning: buggy
}

There’s an exceedingly good chance that this code will hang your application.  The Task<string>.Result property is strongly typed as a String, and thus it can’t return until it has the valid result string to hand back; in other words, it blocks until the result is available.  We’re inside of button1_Click then blocking for LoadStringAsync to complete, but LoadStringAsync’s implementation depends on being able to post code asynchronously back to the UI to be executed, and the task returned from LoadStringAsync won’t complete until it does.  LoadStringAsync is waiting for button1_Click to complete, and button1_Click is waiting for LoadStringAsync to complete. Deadlock!

This problem can be exemplified easily without using any of this complicated machinery, e.g.:

private void button1_Click(object sender, RoutedEventArgs e)
{
    var mre = new ManualResetEvent(false);
    SynchronizationContext.Current.Post(_ => mre.Set(), null);
    mre.WaitOne(); // warning: buggy
}

Here, we’re creating a ManualResetEvent, a synchronization primitive that allows us to synchronously wait (block) until the primitive is set.  After creating it, we post back to the UI thread to set the event, and then we wait for it to be set.  But we’re waiting on the very thread that would go back to the message loop to pick up the posted message to do the set operation.  Deadlock.

The moral of this (longer than intended) story is that you should not block the UI thread.  Contrary to Nike’s recommendations, just don’t do it.  The new async language functionality makes it easy to asynchronous wait for your work to complete.  So, on your UI thread, instead of writing:

Task<string> s = LoadStringAsync();
textBox1.Text = s.Result; // BAD ON UI

you can write:

Task<string> s = LoadStringAsync();
textBox1.Text = await s; // GOOD ON UI

Or instead of writing:

Task t = DoWork();
t.Wait(); // BAD ON UI

you can write:

Task t = DoWork();
await t; // GOOD ON UI

This isn’t to say you should never block.  To the contrary, synchronously waiting for a task to complete can be a very effective mechanism, and can exhibit less overhead in many situations than the asynchronous counterpart.  There are also some contexts where asynchronously waiting can be dangerous. For these reasons and others, Task and Task<TResult> support both approaches, so you can have your cake and eat it too.  Just be cognizant of what you’re doing and when, and don’t block your UI thread.

(One final note: the Async CTP includes the TaskEx.ConfigureAwait method.  You can use this method to suppress the default behavior of marshaling back to the original synchronization context.  This could have been used, for example, in the LoadStringAsync method to prevent those awaits from needing to return to the UI thread.  This would not only have prevented the deadlock, it would have also resulted in better performance, because we now no longer need to force execution back to the UI thread, when nothing in that method actually needed to run on the UI thread.)

Leave a Comment
  • Please add 7 and 6 and type the answer here:
  • Post
  • Hi,

    May you explain or point to an explanation of how it's implemented. In particular, does await execute message loop recursively or it switches stack? As far as I understand, it introduces implicit reentrance, so how handles:

    private void button1_Click(object sender, RoutedEventArgs e)

    {

    mutex.lock();

       string s = await LoadStringAsync();

       textBox1.Text = s;

    mutex.unlock();

    }

    ?

    Or:

    private void button1_Click(object sender, RoutedEventArgs e)

    {

    must_be_even += 1;

       string s = await LoadStringAsync();

       textBox1.Text = s;

    must_be_even += 1;

    }

    private void button2_Click(object sender, RoutedEventArgs e)

    {

    assert((must_be_even % 2) == 0);

    }

    ?

    Thank you.

  • "await" ends up transforming a method in a very similar manner to how C# iterators work.  Think of "await" as being an asynchronous version of "yield return", where you'll only come back into the function when the task that's been awaited (or in the yield return analogy, yielded) completes.  "await" does not introduce a nested message loop (as would happen with Application.DoEvents in Windows Forms, or Dispatcher.PushFrame in WPF), but rather if you await inside a UI event handler (like button1_Click in the examples) ends up returning control to the existing message loop.  State about where the function was in its execution, what "local"s were storing, etc., is all maintained in a data structure off to the side until execution is ready to resume, at which point a new function call is made to continue execution where the previous one left off.

    Using locking around an "await" will typically lead to problems.  What happens in your first example will depend on whether mutex is a reentrant lock or not (i.e. if you call lock from the thread that's already holding the lock, does that call enable acquiring the mutex again, or does it block waiting for the mutex to be released).  If the mutex in your example is not reentrant (as is the case with Mutex in .NET), then if you were to call button1_Click twice in quick succession, the first call would acquire the mutex, call LoadStringAsync, and then return until the loading had completed.  Then the same thread would process the second click event, and would deadlock trying to acquire the mutex again.  The only way this would get unblocked is for the initial load event to complete and proceed to release the mutex, but as mentioned in my post, the remainder of the function will be posted back to the UI thread to continue execution, and that contnuation won't be able to run until the second click event handler returns, but it's blocked waiting on the first.  Deadlock.  If the mutex event is reentrant, then because you're only using this mutex from one thread (the UI thread), such that no other thread will ever try to acquire the mutex, it's effectively a nop.

    Your specific example aside, however, locking around an "await" is not a good idea.  Your example was special because there was only one thread involved.  An arguably worse case would be if you were doing this on thread pool threads, as the continuation after the await is in no way guaranteed to return to the exact same thread the load operation started on, and thus you could (a) be trying to release the mutex on a thread that didn't acquire it, and (b) leave the mutex acquired and likely hang the system when other threads try to acquire it (since they'll end up blocking indefinitely).

    For your second example, remember that a primary reason for being able to await like this on your UI thread is to retain responsiveness, which means not blocking other messages from being processed.  That means that if you don't want button2_Click to be callable while you're awaiting LoadStringAsync in button1_Click, in button1_Click you should disable button2 before the await and enable it afterwards.  Otherwise, your assert in button2_Click could fail if button2_Click is called while you're awaiting LoadStringAsync in button1_Click.  In short, you need to make sure that invariants are not broken across await points in the UI thread, because by design other code will be able to run on the UI thread in those intervals and observe the broken invariant.

  • Thank you for the exhaustive answer!

    So the state is captured in a separate object. But how a scope of the continuation is determined? Is it determined by the function which calls await? That is, await returns from the current function, and continues execution of a outer function; then continuation executes code from await till end of the function and terminates?

    What if I need to call await in a deeply nested function and extend continuation scope to several functions?

    Regarding the mutex and assert, ok, I get it. I am just thinking about calling await in deeply nested functions, where mutexes, invariants and reenterancy issues can be quite unclear. So, if I call await in the top level logic (like GUI event handler), everything is Ok (if I take into account it's asynchronous nature, that is don't hold a mutex around await, for example). But if I call await in a deeply nested function... well, can't it lead to problems?

  • re: "That is, await returns from the current function, and continues execution of a outer function; then continuation executes code from await till end of the function and terminates?"

    The compiler transforms the method into a state machine that allows it to jump to a point after any await.  As such, when the thing you awaited completes, the compiler in effect calls the method again, but does so in a manner that immediately jumps to just after the await that caused the previous invocation to return.  Execution will then continue to the next await that yields, and which point you'll again return out of the function, and when the thing that was awaited completes, the method will again be called, jumping immediately to just after that await, and continuing.  And so on.  This process continues until the state machine ends, i.e. until there is nothing more to execute due to the function ending.  At that point the task returned from the async method completes.

    A good document for you to read is: go.microsoft.com/fwlink.  This provides more insight into the compiler transformations applied.

    re: "What if I need to call await in a deeply nested function and extend continuation scope to several functions?"

    That's fine, and is very much expected.  Again,I think the document referenced above should help, as well as this one:go.microsoft.com/fwlink.

    re: "But if I call await in a deeply nested function... well, can't it lead to problems?"

    The key is not to await while an invariant is broken, just like you shouldn't "yield return" out of an iterator when an invariant is broken (or, as in the case of the UI example, if you need to await when an invariant is broken, make sure that no one can observe the brokeness).  The primary reason we developers use synchronization primitives like a mutex is to turn something that wasn't atomic into something that's visibly atomic, and that's done because we have an invariant in our program we need to maintain.  Inserting an await at a point when there's a broken invariant is what leads to the problems you're suggesting.

    This doesn't just apply to awaits of course.  Imagine I have two functions:

    void Foo()

    {

       lock(syncObj)

       {

           Assert(m_value % 2 == 0);

       }

    }

    void Bar(Action action)

    {

       lock(syncObj)

       {

           m_value++;

           action();

           m_value++;

       }

    }

    If I then run the code Bar(Foo), I'll be invoking Foo while holding the lock, which will allow Foo to see the broken invariant that m_value is odd when it's always visibly supposed to be even.  This is very similar to awaiting while holding a lock or mutex, because you're allowing arbitrary code to run while the sync primitive is held.

    I hope that helps.

  • Thank you for the answer and the references. I think that I finally grasp the concept. So I can "prolong" the context by adding "async" to declarations of outer functions.

    Regarding reenterancy, I agree that it's possible to get the same issues with explicit function calls, however, well, they are at least explicit. While with 'await' I as if call one function, but in reality other functions are called at this point in execution. There are 2 contradictory motives in the context of concurrency, on one hand one want to lessen syntactic burden, but on other hand one does not want concurrency to be too implicit. At least in this model. For example, in Erlang there are also 'async' calls, but they do not lead to reenterancy, that is a thread synchronously waits for a response... never mind, I am just thinking aloud.

  • Is there a problem with the first call?

    With this code:

    private async void button1_Click(object sender, RoutedEventArgs e)

           {

               WebClient client = new WebClient();

               textBlock1.Text = await client.DownloadStringTaskAsync(textBox1.Text);

           }

    it freezes my interface, but after the first call it runs assynchronously.

    Thanks

  • Hi Murilo-

    Is this only happening when the debugger is attached?  If it happens regardless of whether the debugger is attached, it's possible you're running into the issue discussed at social.msdn.microsoft.com/.../9f75f92c-9a51-4921-bad5-97c2b15870bd. If that is the case, and if you can't fix it through other means, you could work around it by wrapping this in another task, e.g.

    string text = textBlock1.Text;

    textBlock1.Text = await Task.Run(() => client.DownloadStringTaskAsync(text));

  • One thing that is still unclear to me is if and when await calls execute on separate threads than the one they are called from. For example, how is it ensured that LoadStringAsync is not executed on the UI thread? What about GetFirstNameAsync? A lot of documentation suggests that a majority of the time, async code will execute in the same thread. Is the UI thread a special case?

  • Hi Jeff-

    When you call an async method, it runs synchronously on the current thread until an await that yields.  At that point, a continuation is hooked up to the awaited object such that the awaited object will invoke the continuation when the operation completes.  It's then up to the awaited object where the continuation will be invoked; one option is for the continuation just to be invoked on whatever thread is completing the async operation, another is for it to marshal the invocation to some other thread, like the UI thread.  The latter is what happens by default if awaiting a Task on the UI thread.

    That's why the docs say that the code will execute in the same thread a lot of the time.  If you do this on the UI thread, the async method will run on the UI thread until the await.  Then while asynchronously waiting for the awaited object to complete, the thread can be used for other things.  Then when the awaited operation completes, the continuation will be posted back to run on the UI thread, such that the remainder of the method runs on the UI thread.

    I hope that helps.

  • Shouldn't this method carry the async keyword:

    private void button1_Click()

  • @Robert Oschler: Yes, the one case where button1_Click was using await, it should have been marked as "async".  I've fixed it now.  Thanks for pointing that typo out.

  • @Stephen Toub - My pleasure.

    Any comments on this sentiment?  I have nearly 30 await warnings in a project of mine because of the frequent need to "fire and forget" async processes that have to run on the UI thread (so no Task.run) that must not block.  Despite the warnings against "async void", I'm seriously considering using it because it gets rid of the warnings.  I read warnings about Exception handling issues with doing so, but it's gotten to the point I'm missing important warnings due to the sea of async not awaited warnings I'm getting with my projects.  For the record, I've seen a few of the same warnings in the Project template code created by MS and other respected developers so how evil can it be?

  • @Robert Oschler: IMO, that warning is important.  Way too often developers write code like:

       Task.Delay(100);

    expecting a delay to happen, but this is a nop because it's not being awaited. If you really want fire-and-forget operations, then async void methods are ok.  Alternatively, you could just define for yourself an Ignore extension method like:

       public static void Ignore(this Task task)

       {

       }

    which you can then tack onto any tasks you're not awaiting: get the warning, tack on ".Ignore()".  And in your Ignore method, you could then do additional things, like hooking up a continuation for the task that'll log any exception it gets, e.g.

       public static void Ignore(this Task task)

       {

           task.ContinueWith(t => Log(t.Exception),

               CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default);

       }

Page 1 of 1 (13 items)