Threading gotcha in C#
So, as I mentioned last time, Gnome Do rocks. So much in fact that I’m looking for ways to contribute to the project. I haven’t been this excited about a piece of software in quite some time. The nerd in me is giddy.
My first contribution was to fix a bug I opened against the JIRA Gnome Do plugin (an awesome plugin by the way). The bug was causing Gnome Do to crash at startup if the JIRA server could not be reached. The fix was pretty easy. Wrap the code throwing the exception that was causing the crash in a try/catch block, and simply log and swallow the exception. Gnome Do calls the plugin every 5 minutes to update its index, so once you eventually connected to the web or logged in to your company’s network and can see your JIRA server, the plugin will be able to index the JIRA issues and all will be peachy.
I tested the fix, and it worked like a champ. However, I started to think to myself that it was very odd that Gnome Do let a rogue plugin take it down. I figured it would be a good idea to isolate all calls to the plugins, and wrap them all in try/catch blocks, as a means to protect the core application. I did some digging in the code, and I found that Gnome Do was already doing exactly that. So why on earth did an exception in the plugin take down the core app?
I did some more digging, and in the JIRA plugin, the update takes place in a separate thread. The author of the JIRA plugin, knowing that the call from Do to update the plugin’s index was a blocking call, and knowing that the communication with JIRA may take a little while, decided to do the communication with JIRA in a different thread, and immediately return control to Do. It was this thread that was throwing the exception when the JIRA server could not be reached. This puzzled me. Could it be possible that an uncaught exception in a separate thread can take down the entire application? “No way in hell” I thought to myself.
I was wrong.
With Google by my side, I searched the web for “c# thread exception” and found this free e-book about threading in C#. At the very bottom of the page, read this:
From .NET 2.0 onwards, an unhandled exception on any thread shuts down the whole application, meaning ignoring the exception is generally not an option. Hence a try/catch block is required in every thread entry method – at least in production applications – in order to avoid unwanted application shutdown in case of an unhandled exception.
Holy crap! I showed this to a co-worker, who quickly whipped up a Java program that spawned a thread that threw an exception, to see what happened. We were pretty sure that only that thread would die, and that the JVM would continue to chug along as normal. Thankfully, we were right.
Why on earth would .NET behave this way? The e-book says “From .NET 2.0 onwards”, so I’m assuming that this wasn’t the case in .NET 1.0. I wonder what the reason was for this change. Now, any .NET application that allows itself to be extended by plugins is now at risk of being taken down, at any time, by a rogue plugin! Yikes!