Aug 10 2011

Async CTP – Be Careful With Exceptions!

Category: Asynchrony,C#,Software DevelopmentWil @ 10:38 pm

I’m certainly no Bill Wagner, Jon Skeet or any of the other extremely intelligent and talented individuals who know their way around the new C# and VB asynchronous “stuff” coming soon from Microsoft. However, why can’t I throw my tattered and torn hat in the ring every now and then right?!

While preparing code for my talk at the upcoming Dallas TechFest 2011, and writing code samples to demonstrate how exceptions work with async and await, I ran across some nastiness that I really don’t think should be working, or in this case… breaking, the way it is in the refresh of the Async CTP. Since I could not find any mention of this issue on any website or forum (I’m not saying they don’t exist), I am going to show you it here.

Without any further ado, exceptions… inside an async method. Exceptions are supposed to propagate to the caller who is awaiting the completion of an async call. But… take this code for example:

public void Run()
{
	try
	{
		MyVoidAsyncMethod(null);

		Console.WriteLine("Press a key...");
		Console.ReadLine(); // <--- BIG problem!
	}
	catch (Exception ex_)
	{
		Console.WriteLine(ex_.Message);
	}
	Console.WriteLine("You'll never see this!");
}

private async void MyVoidAsyncMethod(string betterNotBeNull)
{
	if (string.IsNullOrEmpty(betterNotBeNull))
		throw new ArgumentNullException("That's why we can't have nice things!");

	await TaskEx.Yield();
}

Here’s what you get if you execute it:

Press a key…

Unhandled Exception: System.ArgumentNullException: Value cannot be null.
Parameter name: That’s why we can’t have nice things!

Server stack trace:
at DallasTechFest_2011.DeleteMe.d__0.MoveNext() in C:\DTF2011\DallasTechFest2011_Async\02 Exception Demos\DeleteMe.cs:line 27

Exception rethrown at [0]:
at System.Runtime.CompilerServices.AsyncVoidMethodBuilder.b__1(Object state)
at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
at System.Threading.ThreadPoolWorkQueue.Dispatch()
at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
Press any key to continue . . .

The UI thread is blocked by the Console.ReadLine() and that totally hoses up the entire system… and it crashes the application immediately. This is an edge case and is definitely one of those, “Doctor, it hurts when I do this. Well, don’t do that!” situations. You probably shouldn’t be blocking the UI thread that called the async method anyway… so say’th the peanut gallery… and I agree.

However, it is inside a try/catch which “appears” to be completely ignored. This all has to do with the way the compiler generates the code for an async method. The first thing it is contractually obligated to do is to create an AsynVoidMethodBuilder object… and it does as you can see from the exception above. But since this is an ‘async voic’ method, it sort of freaks out… which I believe (ultimately) it shouldn’t.

Now, if you replace the async method with one that returns a Task<> like this:

private async Task<int> MyTaskAsyncMethod(string betterNotBeNull)
{
	if (string.IsNullOrEmpty(betterNotBeNull))
		throw new ArgumentNullException("That's why we can't have nice things!");

	await TaskEx.Yield();

	return 42;
}

Then… all you get for output is:

Press a key…

Moral? Not sure… maybe it’s don’t block your UI?! Maybe it’s just a hole that will be filled shortly by the C# compiler team before C# 5 ships. It’s worth noting at least. You’ve been warned!

Tags: ,


Get Adobe Flash playerPlugin by wpburn.com wordpress themes