PreviousNext

Rules and Conventions for Modular Use of Exceptions

The following rules ensure that exceptions are used in a modular way so that independent software components can be written without requiring knowledge of each other:

· Use unique names for exceptions.

A naming convention makes sure that the names for exceptions that are declared EXTERN from different modules do not clash. The following convention is recommended:

<facility-prefix>_<error_name>_e

For example, pthread_cancel_e.

· Avoid putting code in a TRY routine that belongs before it.

The TRY only guards statements for which the statements in the FINALLY, CATCH, or CATCH_ALL clauses are always valid.

A common misuse of TRY is to put code in the try_block that needs to be placed before TRY. An example of this misuse is as follows:

TRY

handle = open_file (file_name);

/* Statements that may raise an exception here */

FINALLY

close (handle);

ENDTRY

The preceding FINALLY code assumes that no exception is raised by open_file. This is because the code accesses an invalid identifier in the FINALLY part if open_file is modified to raise an exception. The preceding example needs to be rewritten as follows:

handle = open_file (file_name);

TRY

{

/* Statements that may raise an exception here */

}

FINALLY

close (handle);

ENDTRY

The code that opens the file belongs prior to TRY, and the code that closes the file belongs in the FINALLY statement. (If open_file raises exceptions, it may need a separate try_block.)

· Raise exceptions to their proper scope.

Write functions that propagate exceptions to their callers so that the function does not modify any persistent process state before raising the exception. A call to the matching close call is required only if the open_file operation is successful in the current scope.

If open_file raises an exception, the identifier will not be written, so open_file must not require that close be called when open_file raises an exception; that is, open_file should not be part of the TRY clause because that means close is called if open_file fails, and you cannot close an unopened file.

· Do not place a RETURN or nonlocal GOTO between TRY and ENDTRY.

It is invalid to use RETURN or GOTO, or to leave by any other means, a TRY, CATCH, CATCH_ALL, or FINALLY block. Special code is generated by the ENDTRY macro, and it must be executed.

· Use the ANSI C volatile attribute.

Variables that are read or written by exception-handling code must be declared with the ANSI C volatile attribute. Run your tests with the optimize compiler option to ensure that the compiler thoroughly tests your exception-handling code.

· Reraise exceptions that are not fully handled.

You need to reraise any exception that you catch, unless your handler performs the complete recovery action for the error. This rule permits an unhandled exception to propagate to some final default handler that prints an error message to terminate the offending thread. (An unhandled exception is an exception for which recovery is incomplete.)

A corollary of this rule is that CATCH_ALL handlers must reraise the exception because they may catch any exception, and usually cannot do recovery actions that are proper for every exception.

Following this convention is important so that you also do not absorb a cancel or thread-exit request. These are mapped into exceptions so that exception handling has the full power to handle all exceptional conditions from access violations to thread exit. (In some applications, it is important to be able to catch these to work around an erroneously written library package, for example, or to provide a fully fault-tolerant thread.)

· Declare only static exceptions.

For compatibility with C++, you need to only declare static exceptions.