Catch, Rethrow and Filters - Why you should care?

Catch, Rethrow and Filters - Why you should care?

  • Comments 52

 

A very common pattern in the usage of managed exception handling is that of catching an exception, inspecting it's type and rethrowing it once you realize it was not the exception you wanted to handle. Below is such an example (and should be avoided in preference to another approach described further below in the writeup) that uses CustomBaseException as the base type of an exception hierarchy and CustomDontWantToCatchException as a type that derives from the base class that you wouldn’t want to catch:

 

    private static void Foo()

    {

        try

        {

            // Set some program state

            // Do some work that can possibly throw an exception

        }

        finally

        {

            // Reset the program state

        }

    }

 

    public static void Main()

    {

        try

        {

            Foo();

        }

        catch (CustomBaseException ex)

        {

            if (ex is CustomDontWantToCatchException)

                throw; // Rethrow since we dont want to handle exceptions we aren’t interested in!

            else

            {

                // handle the exception

            }

        }

    }

 

Managed exception handling comprises of two passes:

 

1.       In the first pass, the CLR looks for a handler for the thrown exception. When one is found, it begins the second pass.

2.       In the second pass, also known as the unwind pass, it invokes all the termination handlers (e.g. managed finally/fault blocks, along with the native counterparts like __finally, destructors of stack allocated objects) that lie between the handler of the exception and the point at which the exception was thrown.

 

Thus, if you use a pattern like the one above, only to rethrow an exception since it was decided not to deal with it, prior to your [catch] handler being invoked, the termination handlers will be invoked. These handlers would end up doing the cleanup (like reset program state) before control is returned to the handler that agreed to handle the exception. Hence, by the time you enter the catch block, like in the example above, program state would have been modified since the finally in Foo would have been invoked. Thus, type handlers (like a catch clause) are invoked after the second pass has successfully been completed even though they are located in the first pass.

 

Assuming the exception was CustomDontWantToCatchException, the catch block proceeds to rethrow it, expecting it to go unhandled. When exceptions go unhandled, it is a good thing - and that is because we get the actual program state at the time when the exception was thrown. However, when pattern like the one above is used to rethrow conditionally, the program state gets modified and when the rethrown exception becomes unhandled, you will see actual program state from the point of rethrow and not the original throw.

 

So, how do we address such conditional exception processing without affecting program state for better diagnostics (in case the exception goes unhandled)?

 

Filters! Not really well known and not used a lot, managed filters are invoked by the CLR in the first pass when it is looking for a handler for an exception. While  a type handler is associated with a concrete type based upon which the CLR will decide whether the handler is capable of handling the exception or not, a filter can have custom logic to determine whether it wants to handle the exception or not.

 

When a filter is found in the first pass, the CLR will pass it the exception object corresponding to the thrown exception. The filter can then decide whether it wants to handle the exception or not, by accessing the state on the exception object (Note: accessing global program state from within a filter may lead to unexpected results. Thus, any such accesses should be avoided from within a filter). Once it has decided, a boolean is returned back to the CLR indicate its decision. If it agrees to handle the exception, the CLR will proceed to trigger the second (or unwind) pass. But if it decides not to handle the exception, the CLR will continue look further on the stack for any handler that may want to handle the exception. If none are found, the exception becomes unhandled and CLR's unhandled exception processing kicks in.

 

Hence, instead of catching all exceptions as shown in the example above (or using base type of a given exception hierarchy) and then rethrowing the caught exception because you didn’t want to handle it, write a filter that will enable you to do just that without triggering the second pass and modifying program state in the process.

 

And how does one write a managed filter?


While the CLR support filters, not all managed languages support it - IL and VB, for instance, do support it but C# does not! Rewriting the Main method above in VB , we can see how easy it can be to inspect an exception on the fly without affecting the program state and conditionally deciding whether, or not, to handle the exception:

 

 Function ShouldCatch(ByVal exception As CustomBaseException) As Boolean

 

        If TypeOf (exception) Is CustomDontWantToCatchException Then

            Return False

        Else

            Return True

        End If
 

End Function

 

Sub Main()

        Try

            Foo()

 

        Catch ex As CustomBaseException When ShouldCatch(ex) = True

 

            Console.WriteLine("Caught exception!")

 

        End Try

End Sub

 

VB's Catch keyword, when used with the When keyword, emits an IL filter as shown in the IL disassembly of the Main function below:

 

.method public static void  Main() cil managed

{

  .entrypoint

  .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 )

  // Code size       62 (0x3e)

  .maxstack  3

  .locals init ([0] class ConsoleApplication1.CustomBaseException ex)

  IL_0000:  nop

  IL_0001:  nop

  IL_0002:  call       void ConsoleApplication1.Module1::Foo()

  IL_0007:  nop

  IL_0008:  leave.s    IL_003b

  IL_000a:  isinst     ConsoleApplication1.CustomBaseException

  IL_000f:  dup

  IL_0010:  brtrue.s   IL_0016

  IL_0012:  pop

  IL_0013:  ldc.i4.0

  IL_0014:  br.s       IL_0026

  IL_0016:  dup

  IL_0017:  stloc.0

  IL_0018:  call       void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.ProjectData::SetProjectError(class [mscorlib]System.Exception)

  IL_001d:  ldloc.0

  IL_001e:  call       bool ConsoleApplication1.Module1::ShouldCatch(class ConsoleApplication1.CustomBaseException)

  IL_0023:  ldc.i4.0

  IL_0024:  cgt.un

  IL_0026:  endfilter

  IL_0028:  pop

  IL_0029:  ldstr      "Caught exception!"

  IL_002e:  call       void [mscorlib]System.Console::WriteLine(string)

  IL_0033:  nop

  IL_0034:  call       void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.ProjectData::ClearProjectError()

  IL_0039:  leave.s    IL_003b

  IL_003b:  nop

  .try IL_0002 to IL_000a filter IL_000a handler IL_0028 to IL_003b

  IL_003c:  nop

  IL_003d:  ret

} // end of method Module1::Main


You can see that, towards the end of the disassembly, we have the IL regions (bold and highlighted in yellow, between IL offset 3B and 3C) defining what constitutes the try block, the filter and if the exception is going to be handled, the filter-handler as well. Depending upon what the filter returns, after invoking ShouldCatch (which, in this example, decides whether the exception should be handled based upon its type), the CLR will either  execute the filter-handler or continue to go up the stack looking for exception handlers.

 

To conclude, for such exception inspection patterns that need to handle exception conditionally, use filters as they enable you to do the same as catching and rethrowing pattern without affecting the program state. It’d be great if they were in C#, but they’re not, so really consider using IL filters or VB where you need this functionality.

 

Gaurav Khanna

Developer,

Common Language Runtime

 

Leave a Comment
  • Please add 7 and 7 and type the answer here:
  • Post
  • VB has a lot of high level features other .Net languages, in particular C# don’t.  Sadly these are

  • What’s got LINQ, but doesn’t have a yield keyword and no Action or multiline lambdas? What’s supposed to be an easy to learn language, but supports exception filters when other supposedly more complex languages don’t? The answer, VB.NET. “Co-evolution”

  • Somebody posted a comment up my blog which contained something along the lines of  “and VB .NET

  • Somebody posted a comment up my blog which contained something along the lines of  “and VB .NET

  • カワイイ子ほど家出してみたくなるようです。家出掲示板でそのような子と出会ってみませんか?彼女たちは夕食をおごってあげるだけでお礼にHなご奉仕をしてくれちゃったりします

  • セレブ達は一般の人達とは接する機会もなく、その出会う唯一の場所が「逆援助倶楽部」です。 男性はお金、女性はSEXを要求する場合が多いようです。これは女性に圧倒的な財力があるから成り立つことの出来る関係ではないでしょうか?

  • 貴方のオ○ニーライフのお手伝い、救援部でHな見せたがり女性からエロ写メ、ムービーをゲットしよう!近所の女の子なら実際に合ってHな事ができちゃうかも!?夏に向けて開放的になっている女の子と遊んじゃおう

  • 家出中でネットカフェやマンガ喫茶にいる女の子たちは、お金が無くなり家出掲示板で今晩泊めてくれる男性を探しています。ご飯を食べさせてあげたり泊めてあげることで彼女たちはHなお礼をしてくれる事が多いようです

  • 当サイトは、みんなの「勝ち組負け組度」をチェックする性格診断のサイトです。ホントのあなたをズバリ分析しちゃい���す!勝ち組負け組度には、期待以上の意外な結果があるかもしれません

  • 男性が主役の素人ホストでは、男性のテクニック次第で女性会員様から高額な謝礼がもらえます。欲求不満な人妻や、男性と出会いが無い女性が当サイトで男性を求めていらっしゃいます。興味のある方はTOPページからどうぞ

  • エロ漫画やエロゲーなどでかわいい女の子が淫らな肉欲に溺れる様子をみて「こんなの現実にあるわけない」そう思った事ありませんか?それが当サイトでは現実に実現できるのです!羨ましさを憶えた2次元の中での出来事。あなたと同じように望む女の子が当サイトに集まっているのです

  • 高級チェリーの夏は童貞卒業の夏です。セレブ達も童貞を卒業させたくてウズウズしながら貴方との出会いを待っています。そんなセレブ達に童貞を捧げ、貴方もハッピーライフを送ってみませんか

  • 何回かメールして会える人一緒に楽しいことしょ?お給料もらったばかりだからご飯くらいならごちそうしちゃうょ♪ cha-a@docomo.ne.jp とりあえずメールくださぃ★

  • セレブラブではココロとカラダに癒しを求めるセレブ達と会って頂ける男性を募集しています。セレブ女性が集まる当サイトではリッチな彼女たちからの謝礼を保証、安心して男性はお金、女性は体の欲求を満たしていただけます。無料登録は当サイトトップページからどうぞ

  • 家出中でお金が無く、ネットカフェを泊り歩いているSOS少女たちは、家出掲示板で泊めてくれたり遊んでくれる男性を探しています。泊めてあげたりすると彼女たちはHなお礼をしてくれるかもしれません。家出少女と遊びたい方は当サイトはどうぞ

Page 2 of 4 (52 items) 1234