Document revision date: 30 March 2001

## Guide to the POSIX Threads Library

Previous | Contents | Index

### 5.3.4 Reraising an Exception

Reraising an exception means to pass it to the next outer exception scope for further processing. Your program should take this step for a given exception when it must respond to the error condition but cannot completely recover from it.

As shown in Example 5-3, within a `CATCH` or `CATCH_ALL` code block, your program can invoke the `RERAISE` macro to pass a caught exception to the next outer exception scope in your program. If there is no next outer `TRY` block, the default handler for unhandled exceptions receives the exception, produces a default error message that identifies the unhandled exception, then terminates the process.

Reraising is particularly appropriate for an exception caught in a `CATCH_ALL` block. Because this code block may catch exceptions that are unexpected by your program's code, it is unlikely that your code is able to fully recover from the error condition that the exception represents. Therefore, your code should allow the exceptions to continue to propagate, either so that it will reach a handler that can deal with it properly or the process can be terminated safely.

### 5.3.5 Expressing Epilogue Actions

Example 5-4 demonstrates the use of the optional `FINALLY` block.

Example 5-4 Defining Epilogue Actions Using FINALLY

       int *local_mem;

       local_mem = malloc (sizeof (int));
       TRY {                      /* An exception can be raised within this scope */
          operation (local_mem);
       }
       FINALLY {
          free (local_mem);
       }
       ENDTRY

A `FINALLY` block catches an exception and implicitly reraises the exception for the next outer exception scope to handle. The actions defined by a `FINALLY` block are also performed on normal exit from the `TRY` block if no exception is raised. This means that those actions need not be duplicated in your code.

Do not combine a `FINALLY` block with either a `CATCH` block or `CATCH_ALL` block in the same `TRY` block.

## 5.4 Exception Objects

This section describes the attributes of exception objects (that is, the `EXCEPTION` type) and the behavior of the exceptions package's exception handling macros (that is, `RAISE` and `RERAISE` , `TRY` , `CATCH` and `CATCH_ALL` , and `FINALLY` ).

An exception object is a data object that represents an error condition that has occurred in a particular context. The error condition can be detected by the operating system, by the native programming language, by another programmatic facility that your program calls, or by your own program. In the exceptions package, it is a statically allocated variable of type `EXCEPTION` .

### 5.4.1 Declaring and Initializing Exception Objects

The `EXCEPTION` type is designed to be an opaque type and should only be manipulated by the exceptions package routines. The actual definition of the type may differ from one release to another. The `EXCEPTION` type is defined in the `pthread_exception.h` header file.

You should declare the type as `static` or `extern` . For example:

       static EXCEPTION an_error;

Because on some platforms an exception object may require dynamic initialization, the exceptions package requires a run-time initialization call in addition to the declaration. The initialization routine is a macro named `EXCEPTION_INIT` . The name of the exception is passed as a parameter.

The following code fragment shows how a program declares and initializes an exception object:

       EXCEPTION parity_error;           /* Declare it */

       EXCEPTION_INIT (parity_error);    /* Initialize it */

### 5.4.2 Address Exceptions and Status Exceptions

By default, when your program raises an exception using an exception object that has been properly initialized, the exception is identified by the address of the exception object. This form of exception object is called an address exception. Your program code that handles address exceptions is fully portable among supported platforms because address exceptions contain nothing that is platform dependent.

Use address exceptions if the error conditions that report in your program do not correspond to a system status code. Address exceptions are always unique, so using them cannot cause a "collision" with another facility's status codes and possibly lead inadvertently to handling the wrong exception.

Alternatively, after initializing an exception object and before the exception can be raised, your program can assign a status value to it. The status value is typically an operating system-specific status code that represents a particular error condition. That is, your program can use the exceptions package's `pthread_exc_set_status_np()` routine to assign a C errno code on Tru64 UNIX or a condition code on OpenVMS to the exception object. This form of exception object is called a status exception.

Given two different exception objects that have been set with the same status value, the exceptions package considers the two objects as representing the same exception. For example, if one of the two objects is used to raise an exception, the exception can be caught by specifying the other exception object that has been set to the same status value. In contrast, the Threads Library never considers two distinct address exception objects to match the same exception.

Using status exceptions can make sense if your program's target platform supports a universal definition of error status. That is, a status exception has the advantage of having some global meaning within your program and with respect to other libraries that your program uses. Your program can interpret, handle, and report the values used in status exceptions in a "centralized" manner, regardless of which facility in your program defines the status value.

### 5.4.3 How Exceptions Terminate

Threads Library exceptions are terminating exceptions. This means that after a thread raises a particular exception, the thread never resumes execution in the code that immediately follows the statement that invokes the `RAISE` macro.

Instead, raising the exception causes the thread to resume execution at the appropriate block of handler code (that is, program statements in a `CATCH` , `CATCH_ALL` or `FINALLY` block) that is declared in the current exception scope. If the handler in the current exception scope contains a `RERAISE` statement, control reverts to the appropriate handler in the next outer exception scope.

Propagation of the exception---that is, transfer of control to an outer exception scope after executing the `RERAISE` statement---continues until control enters a `CATCH` or `CATCH_ALL` block that does not end with a `RERAISE` statement; after that block's statements are executed, program execution continues at the first statement after the `ENDTRY` statement that terminates that exception scope.

When any thread raises an exception, if no exception scope in that thread handles the exception without reraising it, the Threads Library terminates the process, regardless of the state of the process' other threads. Termination prevents the unhandled error from affecting other areas of the process.

## 5.5 Exception Scopes

An exception scope serves two purposes:

- It defines a lexical scope within your program where it can respond either to a specific raised exception or to any raised exception.
- It also associates this lexical scope with a set of exception handlers. Each of an exception scope's handlers is a code block enclosed within a Threads Library reserved macro, as described in Section 5.7.

Use the `TRY` / `ENDTRY` pair of macros to define an exception scope. (Throughout the discussion, this pair of macros is referred to simply as the `TRY` macro.) The `TRY` macro defines the beginning of an exception scope, and the `ENDTRY` macro defines the scope's end.

Example 5-5 illustrates how a program defines an exception scope that encloses one operation, a call to the `read_tape()` routine.

Example 5-5 Defining an Exception Scope

       EXCEPTION parity_error;

       int  my_function(void)
       {
          TRY {             /* Beginning of exception scope */
             read_tape ();  /* Operation(s) whose execution can raise an exception */
          }
          ENDTRY            /* End of exception scope */
       }

       int  read_tape(void)
       {
          int ret;

          if (tape_is_ready) {

             EXCEPTION_INIT (parity_error);     /* Initialize it */
             ret = read(tape_device);
             if (ret = BAD_PARITY)
                RAISE (parity_error);           /* Raise it */
          }
       }

Defining an exception scope identifies a block of code in which an exception will be handled if it is raised. Any exception raised within the block, or within any routines called directly or indirectly within the block, will pass through the control of this scope.

Because your program can detect different error conditions at different points in the code, your program can define more than one exception scope within its routines.

One exception scope cannot span the boundary of another exception scope. That is, it is invalid for one exception scope to contain only the beginning (the invocation of the `TRY` macro) or end (the invocation of the `ENDTRY` macro) of another exception scope. However, they may be nested--in fact, you can use `TRY` blocks not only inside other `TRY` blocks, but inside `CATCH` and `FINALLY` blocks as well.

## 5.6 Raising Exceptions

After your program declares and initializes an exception object, your program raises that exception when it detects an error condition. Use the exceptions package's `RAISE` macro to raise an exception.

When your program raises an exception, it reports an error not by returning a value, but by propagating the exception. Propagating an exception takes place in a series of steps, as follows:

1. The program searches in the current scope, then in the next outer scope and so on, for an exception handler that explicitly or implicitly responds to the error (such as a `CATCH` , `CATCH_ALL` or `FINALLY` block).
2. The program invokes the handler code that is found.
3. If the exception is reraised, then the process resumes with the first step and the next outer scope.

If the exception scope within which an exception is raised does not define a handler block, then the Threads Library simply "tears down" the current execution scope as the exception propagates up the stack of exception scopes. This is also referred to as "unwinding" the stack.

Example 5-6 illustrates how a program raises an exception.

Example 5-6 Raising an Exception

       error = get_data();
       if (error) {
          EXCEPTION parity_error;           /* Declare it */

          /* Initialize exception object and
              optionally set its status code */

          EXCEPTION_INIT (parity_error);
          pthread_exc_set_status_np (&parity_error, ENOMEM);
          RAISE (parity_error);             /* Raise it */
       }

Threads Library exceptions are classified as terminating exceptions because after an exception is raised even if it is handled, the thread does not resume its execution at the point where the error condition was detected. Rather, execution resumes within the innermost exception scope that defines a handler block that either explicitly or implicitly matches that exception, or that defines an epilogue block for finalization processing. See Section 5.4.3 for further details.

## 5.7 Exception Handling Macros

The exceptions package allows your program to define an exception scope and to define and associate one or more blocks of code, each called an exception handler, with that scope. The exception handler takes appropriate actions in response to an error condition. "Appropriate actions" can mean merely cleaning up a routine's local context and propagating the exception to the next outer exception scope, or it can mean fully responding to the error in such a manner that allows the routine with the handler to continue its work.

### 5.7.1 Context of the Handler

An exception handler always runs within the context of the thread that generates the exception. Exceptions are synchronous events, like an access violation or segmentation fault, that are tied to a specified thread's context.

Exception handlers are also closely tied to the execution context of the block that declares the handler. Thus, in the exceptions package, exception handlers are attached, which means that the handler code appears within the same routine where the specified exceptions are raised (directly or indirectly). This allows the code to access local commands when an exception occurs with that exception scope, and allows the error handling code to be positioned "close" to the code with which it is associated for readability and maintainability.

### 5.7.2 Handlers and Macros

Unlike a signal handler routine, an exception handler can call any pthread routine.

Exception handler code is invoked when a matching exception propagates within the execution scope of the associated exception scope.

Use the exceptions package's `CATCH` macro to define an exception handler code block that is invoked when an exception matching the macro's specified exception object is propagated within the associated exception scope. Use the exceptions package's `CATCH_ALL` macro to define an exception handler code block that is invoked when any other exception is propagated within the associated exception scope.

An exception handler's code can reraise an exception. That is, the code can propagate an exception to the next outer exception scope for further processing. Use the exceptions package's `RERAISE` macro to do so. If appropriate, a handler may instead use the `RAISE` macro to raise a different exception.

Another form of exception handler code is finalization code, or epilogue code. You can define a block of epilogue code and associate it with an exception scope. When an exception is raised, epilogue code performs your cleanup actions within the current exception scope (such as releasing resources), then automatically propagates the raised exception to outer scopes for further processing. Additionally, finalization occurs even if no exception was raised, so that resources are always released without duplication of code.

Use the exceptions package's `FINALLY` macro to define an epilogue code block. Note that, for a given exception scope, `FINALLY` blocks and `CATCH` and `CATCH_ALL` blocks are mutually exclusive.

Each of these macros is discussed in greater detail in the following sections.

### 5.7.3 Catching Specific Exceptions

The exception scope can express interest in catching a particular exception by specifying a corresponding exception object as the argument in a statement that invokes the `CATCH` macro. When an exception reaches the exception scope, control is transferred to the first `CATCH` code block that specifies a matching exception object. If there is more than one `CATCH` code block that specifies a matching object within a single `TRY/ENDTRY` scope, only the first one gains control. (Thus, there is no point in having two `CATCH` blocks with matching or equivalent exceptions.)

To catch an address exception, the `CATCH` macro must specify the name of the exception object used in the invoked `RAISE` macro. However, status exceptions can be caught using any exception object that has been set to the same status code as the exception that was raised.

Example 5-7 shows an exception scope with one exception handler that uses the `CATCH` macro to catch a specific exception ( `parity_error` ) and to specify a recovery action (produce a message).

Example 5-7 Catching a Specific Exception Using CATCH

       TRY {
          read_tape ();
       }
       CATCH (parity_error) {
          printf ("Oops, parity error, read aborted\n");
          printf ("Try cleaning the heads!\n");
          RERAISE;
       }
       ENDTRY

In this example, after catching the exception and executing the recovery action, the handler explicitly reraises the caught exception. This causes the exception to propagate to the next outer exception scope.

Typically, you code one exception handler for each distinct error condition that can be raised anywhere in the program's execution within the associated exception scope.

If it is appropriate for the caught exception to be propagated to the next higher exception scope, the `CATCH` code block can use the `RERAISE` macro as its last action to explicitly raise the same exception again.

### 5.7.4 Catching Unspecified Exceptions

The exception scope can express interest in catching all exceptions by coding an exception handler that uses the `CATCH_ALL` macro.

There must be only one `CATCH_ALL` code block within an exception scope. Note that it is invalid for a `CATCH` macro to follow a `CATCH_ALL` macro within an exception scope.

Example 5-8 demonstrates using the `CATCH_ALL` macro to define an exception handler for expressing actions in response to exceptions that are not being uniquely handled on a per-exception basis in the program's code.

Example 5-8 Catching an Unspecified Exception Using CATCH_ALL

       int *local_mem;

       local_mem = malloc (sizeof (int));
       TRY {
          operation(local_mem);
          free (local_mem);
       }
       CATCH (an_error) {
          printf ("Oops; caught one!\n");
          free (local_mem);
       }
       CATCH_ALL {
          free (local_mem);
          RERAISE;
       }
       ENDTRY

Because you cannot necessarily predict all possible exceptions that your code might encounter, you cannot assume that your code can recover in every possible situation. Therefore, your `CATCH_ALL` code block should explicitly reraise each caught exception as its final action; this allows an outer exception scope also to catch the same exception and to respond appropriately for its own context.

### 5.7.5 Reraising the Current Exception

Within an exception scope's `CATCH` or `CATCH_ALL` code blocks, you can invoke the `RERAISE` macro to reraise a caught exception. This allows the next outer exception scope to handle the exception as it finds appropriate. Invoking the `RERAISE` macro is valid only within a `CATCH` or `CATCH_ALL` code block.

Use the `RERAISE` macro in a `CATCH` or `CATCH_ALL` code block that must restore some permanent program state (for example, releasing resources such as memory or a mutex) but does not have enough context about the detected error condition or sufficient reason to attempt to recover fully. For example, a `CATCH_ALL` code block should always reraise the caught exception as its last action, because the exception handler cannot recover fully from the error since it does not know what the error specifically was.

Refer to Example 5-8 for an example of how a program invokes the `RERAISE` macro as the last action in a `CATCH_ALL` code block.

### 5.7.6 Defining Epilogue Actions

Some of your program's `CATCH` or `CATCH_ALL` code blocks may catch exceptions only for the purpose of performing cleanup actions, such as releasing resources. In many cases, these actions are performed when the `TRY` code block exits normally or after an exception has been caught. This requires duplicating code in the `CATCH_ALL` code block and following the exception scope (for the case when an exception does not occur).

The exceptions package's `FINALLY` macro defines a code block that catches an exception and then implicitly reraises that exception for the next outer exception scope to handle. The actions in a `FINALLY` code block are also performed when the scope exits normally (that is, when no exception is raised), so that they need not be coded more than once.

Example 5-9 demonstrates the `FINALLY` macro.

Example 5-9 Defining Epilogue Actions Using FINALLY

       pthread_mutex_lock (&some_object.mutex);
       some_object.num_waiters = some_object.num_waiters + 1;
       TRY {
           while (! some_object.data_available)
               pthread_cond_wait (&some_object.condition, &some_object.mutex);
           /* The code to act on the data_available goes here */
       }
       FINALLY {
           some_object.num_waiters = some_object.num_waiters - 1;
           pthread_mutex_unlock (&some_object.mutex);
       {
       ENDTRY

In this example, if the thread was canceled while it was waiting, the `pthread_cancel_e` exception would propagate out of the `pthread_cond_wait()` call. The operations in the `FINALLY` code block release the mutex, after ensuring that the shared data associated with the lock is correct for the next thread that acquires the mutex.

Note Do not define a `FINALLY` code block if your exception scope uses a `CATCH` or `CATCH_ALL` code block. Doing so results in unpredictable behavior.

Previous | Next | Contents | Index

| privacy and legal statement |
6101PRO_010.HTML