Async Wandering Part 3 — Awaiting in synchronous WinForms code

This is the third part of the Async Wandering series. For your convenience you can find other parts in the table of contents in Part 1 – Why creating Form from WinForms in unit tests breaks async?

async and await are great features of C#. By using them we can hide all cumbersome details of callbacks and write code in “synchronous” way. However, without good understanding of internals we might quickly run into troubles. Especially if we want to introduce asynchronous methods in synchronous context. Let’s see.

Challenge

Imagine that we have synchronous method in WinForms codebase. Actual technology is not that important, in WPF you might quickly get the same problem since it uses similar concepts.

Now imagine, that you want to run some lengthy action and you would like to have it asynchronous, however, before ending your job you would like to wait for the results. Snippet representing the idea:

SynchronousAction runs on UI thread, LengthyAction might run somewhere else. This solution might be sufficient for our needs (if lengthy action is not that lengthy or else we will freeze the UI), however, imagine now, that action does the following:

This quickly leads to a deadlock.

Deadlock?

Invoke works by posting message to other thread’s message loop and waits until it is handled. In our case, UI thread cannot process the message because it is blocked on task.Wait() method. If we would use await, then we would have no such problem — await in WinForms awaits for tasks on message pump and is able of processing messages. This is very important detail which you don’t need to know as long as you do not block UI thread explicitly. General rule of thumb is: if you use async somewhere in your codebase, you must use it all the way up. Every method must be asynchronous or else you need to be very careful with waits.

Ok, so how can we solve this problem without rewriting everything to asynchronous methods? Well, we can do some dirty magic.

Running message pump manually

Instead of waiting for a task indefinitely, we will perform busy waiting and run message loop by hand:

Instead of running the method directly, we create another task with asynchronous lambda. This new task awaits for lengthy action and sets signal when it is done. We are guaranteed that the second task will not set the signal too early, because we can simply await for our lengthy action.

Next, we cannot block UI thread. Instead of waiting indefinitely, we sleep for a millisecond, call message pump and retry the operation. We will not move forward until the signal is set, however, if lengthy action posts a message to our thread, we will execute it with a little delay (one millisecond here).

Summary

Does it work? Yes. Is it nice? Well, we might argue here. Introducing asynchronous code in synchronous codebase is not easy, we cannot simply replace all methods with asynchronous ones because sometimes it is technically impossible. How do you await for asynchronous event handlers? And if you know (since it is possible but cumbersome), how do you do that in VB.NET (in that language event cannot return anything, so you don’t have a task to wait for). This snippet might save your life one day, but you had better don’t need to try.

Bonus chatter: WPF doesn’t have a Application.DoEvents() for running message pump, however, it looks like it is possible to achieve similar effect with this code.