Alois Kraus

blog

  Home  |   Contact  |   Syndication    |   Login
  133 Posts | 8 Stories | 368 Comments | 162 Trackbacks

News



Archives

Post Categories

Programming

Error handling is a difficult beast. And there is always one more way to do it. Thottam Sriram did write some nice examples what main cases you usually have to deal with. The most ugly case is the one where you cannot handle an exception but you need to do different cleanup logic in a finally block.

 

               public void Func()

        {

            bool cleanup = true;

 

            try

            {

                OtherFunc();

                cleanup = false;

            }

            catch (Exception)

            {

                // perform Cleanup on error

            }

            finally

            {

                if (cleanup)

                {

                    // perform cleanup on error

                }

            }

        }

 

The necessity to keep a state variable for a corner case does complicate the code quite a lot. You can get around that by knowing that .NET exceptions just wrap SEH (Structured Exception Handling) of Windows. The CLR does make extensive usage of SEH which you can read in the wonderful article from Chris Brumme. To sum it up in a few words if an exception is thrown an Exception Pointers structure is allocated. The actual exception processing is done two in two steps.

  1. Ask all exception handlers what about the current error can be done. If one says it can fix it the exception processing is stopped. No exception will ever happen.
  2. If no handler could fix it in the first pass they are actually invoked one by one and the stack unwinding does start.

C++ has special keywords for SEH named __try/__except/__leave which cannot be mixed with the regular try/finally C++ keywords within one function because only one exception handling chain within a function does make sense. Now you could ask how that complicated stuff can make by error handling easier? Well since we know that during an exception unwind scenario for the executing thread an Exception Pointers structure must exist we can check for its existence and we can distinguish the non exception case from the other. The BCL gives us direct access to this precious information via Marshal.GetExceptionPointers. Armed with that knowledge we can create a simple helper class that returns true if we are in an exception unwind case.

 

    public static class ExceptionHelper

    {

        /// <summary>

        /// Check if we are in a exception unwind scenario or not.

        /// </summary>

        public static bool InException

        {

            get

            {   // Errata: The red marked code seems to be necessary. Since unit tests with .NET 2.0

                // have shown that only checking for the Exception Pointers structure does not always work.

                return Marshal.GetExceptionPointers() == IntPtr.Zero &&

                       Marshal.GetExceptionCode() == 0 ? false : true;

            }

        }

    }

 

The code from above becomes now

 

        public void ImprovedFunc()

        {

            try

            {

                OtherFunc();

            }

            finally

            {

                if (ExceptionHelper.InException)

                {

                    // perform cleanup on error

                }

                else

                {

                    // Do normal cleanup in non exception case

                }

            }

        }

 

We did get rid of the catch clause and the state variable. That little trick can make your life easier in some situations. You can call the check property not only in a finally clause but also from a helper function because the Exception Pointers structure is thread local and visible as long exception processing is going on.

 

Remarks

 

At CLR IL (Intermediate Language) level there is the fault keyword which is similar. It acts like a finally block that is only executed when an exception has occurred. Besides the fact that C# does not support it is not really something that I would want. When an error happens I must execute nearly the same cleanup actions as in the non exceptional case. For that reason code sharing within one code block is essential to prevent code duplication both handlers. If you try to be smart and use the goto keyword (yes I won't dare to use it if it would have worked) to jump from a from a catch handler into the finally handler to skip a portion of the cleanup code you will get a compile error.

Since we have got our hands on the Exception Pointers structure we should be able to find there some reference to the managed exception and perhaps even return it. I tried that and failed. By looking at the Rotor source code I could not find evidence that this structure does contain any hints to the managed exception object. It looks like that the managed Thread implementation has it stored somewhere but since CLR objects are a mixture of IL code and C++ classes it is not feasible to use a simple fixed offset and cast it to the managed exception object.

If somebody has more info about that I am always interested to learn more ...

posted on Tuesday, April 8, 2008 11:37 AM

Feedback

# re: Try/Finally Pattern - Take a different path when an exception has occurred. 4/9/2008 3:32 AM Marcus Griep
The open source .NET language Boo does provide support for the CIL 'fault' handler. In the case presented above, where the cleanup is different than in the non-exceptional case, then the fault handler is quite useful. If you want to perform similar clean up, you can do this (using the Boo syntax)
try:
try:
raise Exception()
failure:
// Clean up after exception
ensure:
// additional clean up for all code

# re: Try/Finally Pattern - Take a different path when an exception has occurred. 4/9/2008 3:33 AM Marcus Griep
Indenting was a little screwy, so here is the example again with 'end' to indicate blocks
try:
try:
raise Exception()
failure:
// Clean up after exception
end
ensure:
// additional clean up for all code
end

Post A Comment
Title:
Name:
Email:
Comment:
Verification: