Exceptions out of Fault/Finally

 

Assumption: This write-up assumes that you are familiar with the managed exception handling constructs (e.g. catch, filter, fault, finally). If not, you may want to read this and also refer to the CLI specification.

 

Managed exception handling exposes constructs to handle an exception (e.g. catch and filter blocks) and also to perform any cleanup required by the application, in case the control flow left exceptionally using either the fault or finally blocks. The subtle difference between the latter two is that finally blocks are executed for both exceptional and non-exceptional execution paths, while fault blocks are executed only during an exceptional execution path. Both, however, are executed in the unwind pass of exception handling.

 

Cleanup using finally blocks (or how finally blocks can be made to behave like fault blocks)

 

A common cleanup pattern involving finally blocks is typically implemented for resource cleanup, as shown below:

 

class Program

    {

        static void Main(string[] args)

        {

            FileStream fs = null;

            bool fDidWorkComplete = false;

 

            try

            {

                DoWork(ref fs, ref fDidWorkComplete);

            }

            catch (ArgumentException ex)

            {

                Console.WriteLine(“Caught: {0}”, ex.ToString());

            }

        }

 

        private static void DoWork(ref FileStream fs, ref bool fDidWorkComplete)

        {

            try

            {

                try

                {

                    fs = File.Create(“d:\\kgk\\Log.txt”);

 

                    // Do something that will generate an exception

                    Console.Write(“Writing {0} to the file”, GetString().ToLower());

 

                    // Write string to the file here

 

                    // If we are here, our work is done. Close the file

                    // and set the flag.

                    fs.Close();

 

                    // Flag that we completed the work we wanted to do.

                    fDidWorkComplete = true;

                }

                finally

                {

                    if (!fDidWorkComplete)

                    {

                        // Close the file if required

                        if (fs != null)

                        {

                            WrapperToCloseFile(fs);

                        }

 

                        // Reset other application state

                    }

                }

            }

            catch (NullReferenceException ex)

            {

                Console.WriteLine(“Caught {0}”, ex.ToString());

            }

        }

 

        private static void WrapperToCloseFile(FileStream fs)

        {

            // Do something that may generate an exception

            throw new ArgumentException(“Exception generated by WrapperToCloseFile”);

            fs.Close();

        }

 

        private static string GetString()

        {

            return null;

        }

    }

 

The key here is the flag fDidWorkComplete. For non-exceptional case, we will be able to finish our work, release any resources (e.g. closing the file stream) and set this flag. Since finally blocks are executed for non-exceptional case as well, the code in finally block checks if fDidWorkComplete was set or not. If an exception occurs before the flag was set, finally block will be invoked (by the CLR’s EH system) provided someone caught the exception and hence, cleanup will be performed. For the non-exceptional case, the execution will fall through in the finally block but won’t do cleanup since the flag is set.

 

Effectively, the snippet above shows how fault blocks can be emulated using finally to perform cleanup during an exceptional case using flags (like fDidWorkComplete) to determine whether certain cleanup code should be executed or not. This enables you to get the semantics of fault blocks in a language—such as C#–that doesn’t support them.

 

Exceptions and invocation of fault/finally blocks under exceptional cases