Subterranean IL: Exception handler semantics

Comments 0

Share to social media

In my blog posts on fault and filter exception handlers, I said that the same behaviour could be replicated using normal catch blocks. Well, that isn’t entirely true…

Changing the handler semantics

Consider the following:

If the filter handler is engaged (true is inserted into the exception dictionary), then the following gets printed to the console:

and if the filter handler isn’t engaged, then the following is printed:

The filter handler is executed first. Hmm, ok. Well, what happens if we replaced the fault block with the C# equivalent (with the exception dictionary value set to false)?

we get this:

The fault handler is executed first, instead of the filter block.

Eh?

This change in behaviour is due to the way the CLR searches for exception handlers. When an exception is thrown, the CLR stops execution of the thread, and searches up the stack for an exception handler that can handle the exception and stop it propagating further – catch or filter handlers. It checks the type clause of catch clauses, and executes the code in filter blocks to see if the filter can handle the exception.

When the CLR finds a valid handler, it saves the handler’s location, then goes back to where the exception was thrown and executes fault and finally blocks between there and the handler location, discarding stack frames in the process, until it reaches the handler.

So?

By replacing a fault with a catch, we have changed the semantics of when the filter code is executed; by using a rethrow instruction, we’ve split up the exception handler search into two – one search to find the first catch, then a second when the rethrow instruction is encountered.

This is only really obvious when mixing C# exception handlers with fault or filter handlers, so this doesn’t affect code written only in C#. However it could cause some subtle and hard-to-debug effects with object initialization and ordering when using and calling code written in a language that can compile fault and filter handlers.