WPF – Random IT Utensils https://blog.adamfurmanek.pl IT, operating systems, maths, and more. Fri, 31 Aug 2018 17:26:41 +0000 en-US hourly 1 https://wordpress.org/?v=6.5.2 Async Wandering Part 3 — Awaiting in synchronous WinForms code https://blog.adamfurmanek.pl/2017/01/07/async-wandering-part-3/ https://blog.adamfurmanek.pl/2017/01/07/async-wandering-part-3/#comments Sat, 07 Jan 2017 09:00:39 +0000 https://blog.adamfurmanek.pl/?p=1962 Continue reading 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:

public void SynchronousAction(){
	// Doing stuff
	var task = LengthyAction(this.Form);
	// Doing even more stuff
	task.Wait(); // We want to make sure that action is done
	return;
}

public async Task LengthyAction(Form form){
	// Crazy stuff going on here
}

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:

public async Task LengthyAction(Form form){
	form.Invoke(someLambda);
}

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:

public void SynchronousAction(){
{
	// Doing stuff
	var signal = new ManualResetEvent(false );
	var task = Task.Run( async () =>
	{
		await LengthyAction(this.Form);
		signal.Set();
	});

	// Doing even more stuff
	while (!signal.WaitOne( TimeSpan.FromMilliseconds(1)))
	{
		Application.DoEvents();
	}
	return;
}

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.

]]>
https://blog.adamfurmanek.pl/2017/01/07/async-wandering-part-3/feed/ 2