Think twice before returning null with lambda.

For the first blog post of 2017, the first after awhile really, I really like to touch on one of the interesting problem that I faced along side with the community of the UWP Community Toolkit over at Github awhile back, on one of the API that I designed for the toolkit – The Dispatcher Helper.

This content touches some deep part of C#, make sure you are familiar with lambda/anonymous function, generics and asynchronous programming before proceeding .

The DispatcherHelper API takes in a function of your choice and execute them on a specified thread dispatcher, usually the UI thread’s dispatcher in most UWP application.  The problem arises when the user pass in a synchronous lambda that return a null when the caller is expecting a non-nullable return type. Normally, this would be a compile-time error, but due to the nature of lambda and API with multiple overloads, this became an unexpected runtime error. First, let take a look at the DispatcherHelper API signature:

public static Task<T> AwaitableRunAsync<T>(this CoreDispatcher dispatcher, Func<Task<T>> function, CoreDispatcherPriority priority = CoreDispatcherPriority.Normal)
public static Task<T> AwaitableRunAsync<T>(this CoreDispatcher dispatcher, Func<T> function, CoreDispatcherPriority priority = CoreDispatcherPriority.Normal)
public static Task AwaitableRunAsync(this CoreDispatcher dispatcher, Func<Task> function, CoreDispatcherPriority priority = CoreDispatcherPriority.Normal)
public static Task AwaitableRunAsync(this CoreDispatcher dispatcher, Action function, CoreDispatcherPriority priority = CoreDispatcherPriority.Normal)

The core of the DispatcherHelper API is this function, AwaitableRunAsync, that has 4 overloads for 4 different parameters type : Action, Func, Func<T>, Func<Task<T>> to handle 4  types of function that C# can have. Action is a function that return void, while Func is its asynchronous equivalent. Func<T> is a function that has a return value and T is its type, with Func<Task<T>> is its asynchronous equivalent.So technically you can pass any function into the API to get it executed on the specified dispatcher to get it executed on the UI thread (or any valid thread with a Dispatcher) in an asynchronously manner – one of the most common problems that .NET Developers have to face these days. Now, consider this call:

1

We have a synchronous lambda which update the NormalTextBlock.Text property and then return null, that is running on the DispatcherHelper.ExecuteOnUIThreadAsync (a wrapper for the AwaitableRunAsync() function) . At first glance, this is pretty normal right?

…Until you see the return type of this call, an int! icon16

And what if I tell you Intellisense and Roslyn don’t complain one bit when you compile this?

2

Of course, when you try to run this, with the initial version of the API, it will throw a NullReferenceException, as it was reported on issue #608, at a lower level when the code try to await the call to get the return result. Precisely , it broke inside the AwaitableRunAsync overload that has accepts a Func<Task<T>>, at this call :

taskCompletionSource.SetResult(await function().ConfigureAwait(false));

(here is the full old implementation).

But wait, wasn’t the lambda supposed to be a synchronous function that return an int? Why does it breaks at the await part of the code? You can’t await a synchronous function! And you can’t return null for a function that is returning integer!

From the compiler (Roslyn)’s stand point, this is a completely valid C# syntax at compile time. Why? It is because of the nature of lambda/anonymous function. Let take a step back, look closely again at the lambda that is being passed into the DispatcherHelper:

int returnedFromUIThread = await DispatcherHelper.ExecuteOnUIThreadAsync<int>(() =>
                {
                    NormalTextBlock.Text = "Updated from a random thread!";
                    return null;
                });

Via generics, we explicitly declare and expect an int returning from the ExecutionOnUIThreadAsync() call (a wrapper for AwaitableRunAsync()). And if you remember the 4 overloads that we have for the AwaitableRunAsync(), only 2 of them can accept functions that have the ability to return an int: One accepts a Func<T> and the other accepts a Func<Task<T>> (and in this case, T is int). Because of the lambda being synchronous, the Func<T> one should have been used right? Why does the code breaks inside the Func<Task<T>> version of AwaitableRunAsync() then? Is this a compiler bug?

Anonymous Function/Lambda gets its signature at compile time, when the compiler evaluates the call that the anonymous function/lambda is created. The compiler looks at what the lambda is returning and the expecting return type of the call to assign a signature for the lambda. In our example, the way of the compiler thinks when it hits the lambda above is:

– Oh, hey. I have a lambda that is returning null, so the return type must be nullable!

– And how many choices do we have in ExecuteOnUIThreadAsync() again? Only 2?

– Any of them is nullable?

– Oh well, I know for a fact that int is NOT nullable, so I just chose the one that is returning Task<int> because that one is definitely nullable!

(crash)

So in other word, our null-return-synchronous lambda was passed inside an overloads that was made to deal with asynchronous function because the compiler has no other choice. When we try to await the call, the function return back null to the await statement , hence the NullReferenceException.

Of course, this is a user-error for returning a null inside an int-returning function, but it is not always clear to the developer that they are doing something that they are not supposed to do. Like I previously pointed out, Intellisense and Roslyn both did not catch this because it is completely legal syntax and logic (in this case at least) and the developer has no way to know that they messed up without good unit testing. And not only our home-grown API is effected by this problem, if you try the same thing with Task.Run(), you will run into the same problem with the same reason.

After a lot of discussion(here, and here), the community has come to an agreement of throwing a much more detailed exception to notify the developer that something has gone terribly wrong, rather than some random NullReferenceException. You can view the detailed here at pull #618 by Lukas Fellechner.

So next time, remember to watch your back if you are returning a null inside a lambda, you might be doing something horribly wrong.

Code on!

Some gabarge collecting…

So, I left this blog gathering dust for over 2 months that I can saw a mushroom on the login page (forgotten password anyone?)

As I am trying to upgrade my blog games, I thought it would be interesting to come back and tell the story of me under hibernate mode for the last 2 months. What I’ve been up to, what I’ve been doing while trying to keep my optimism that people would ever think that is interesting.icon9

1. Destination: Bellevue of Beautiful View

WP_20160625_13_13_27_Pro
Not even sure where this is, but shortly before landed at SEATAC – Lumia 950XL ISO50 – 1/1000s – f/1.9

 

After 4 years (and a few months short), I left the city of the Cougars for the east side of Seattle – the beautiful and peaceful Bellevue, where I would remain for the foreseeable future as my *much-anticipated* job is just right further down the road from where I’m currently staying.

There is something about Bellevue that seem to attract me more than Seattle, even though it is a smaller city compare to the home of the Mariners. It is quiet, it is peaceful, it not too crowded or too isolated (ehem, Pullman) and i really felt that like I’m at home.

2. Life current = new Life() { BuyEverything = true };

I spent the last 6 years of going on and off the US without much to worry about walking into a truly empty room. No bed, no desk, no chair , no nothing, not even toilet paper. Ugh. I remembered, sitting down on the carpeted floor, my phone ran out of juice and my stomach rumbled like crazy because I left Pullman in such a hurry that I didn’t even have time for a hotpocket. The reality struck me hard.

Then piece by piece, I turned it around. I’m still amazed with all the stuffs that I have to get in order to basically stay afloat “just enough”. I have been on and off the US in the last 6 years to have stuffs like bedding, toiletries, trash cans, silverware, dishware… taken care of by either my dorm manager or by my roommate (fortunate, I know). I even read the manual of a freaking fan!  icon10

But hey, “whatever float your boat” they said.

 

3. New Project! USCIS Case Tracker for Universal Windows Platform!

I had 2 months of waiting for USCIS to reach and process my case so I can get the EAD Card (or Employment Authorization Document card). Every foreigners in United States need this card if they want to work unless you have working visas or green card (mind you that at this point I’m still being considered as “student”, hehicon14)

So much hate for them for forcing me to wait nearly 3 months, I used all of my waiting time and hatred toward the slow-like-snail system to write an app that at least take some of the heat away from my brain. My UWP game was going strong, and I want to further explore the “adaptive UI” aspect of UWP that my “Notes on Band” could not fulfill due to the fact that the Microsoft Band was only syncing to the Phone via Bluetooth. They recently introduced Microsoft Health to the PC and allowing the Band to be synced via the USB cable as well but I’ve not tried to explore further yet.

Details
Not my case ID, but you know… it works.

The app is written as pure UWP Application, using Master-Detail technique with a Hamburger button that Microsoft seem to be heavily favored lately. It is currently under Pre-alpha and I consider Pre-alpha to be anything that is currently functioning on the basic level of what it is designed to do but still need much more polishing. More on this on a separate blog post that’s coming soon.

 

And phew! That’s it! I hope it is somewhat interesting for a few people who actually read this entire thing (thank you for doing so). I’ll try to stay active more often with my blogging game and there are sure more things to come!

tusky43-1

 

Tuzki emo from : http://photobucket.com/gifs/tuzki?page=1