Tru64 UNIX
Guide to the POSIX Threads Library


Previous Contents Index

5.8 Operations on Exceptions

In addition to raising, catching, and reraising exception objects, the exceptions package supports the following API-level operations on exception objects:

The following sections discuss these operations.

5.8.1 Referencing the Caught Exception

Within a CATCH or CATCH_ALL code block the caught exception object can be referenced by using the THIS_CATCH symbol. You cannot use THIS_CATCH in a FINALLY code block because there might not be an exception.

The THIS_CATCH definition has a type of EXCEPTION * . This value can be passed to the pthread_exc_get_status_np() , pthread_exc_report_np() , or pthread_exc_matches_np() routines, as described in Section 5.8.3, Section 5.8.4, and Section 5.8.5.

Note

Because of the way that the exceptions package propagates exception objects, the address contained in THIS_CATCH might not be the actual address of an address exception. To match THIS_CATCH against known exceptions, use the pthread_exc_matches_np() routine, as described in Section 5.8.5. Furthermore, the value of THIS_CATCH may become invalid when control leaves the CATCH or CATCH_ALL block.

5.8.2 Setting a System-Defined Error Status

Use the pthread_exc_set_status_np() routine to set a status value in an existing address exception object. This converts an address exception object into a status exception object.

This routine's exception object argument must already have been initialized with the exceptions package's EXCEPTION_INIT macro, as described in Section 5.3.1.

In a program that uses status exceptions, use this routine to associate a system-specific status value with the specified exception object. Note that any exception objects set to the same status value are considered equivalent by the Threads Library.

Example 5-10 demonstrates setting an error status in an address exception object.

Example 5-10 Setting an Error Status in an Exception Object

 
   static EXCEPTION an_error; 
 
   unsigned long status_code = ENOMEM; 
   EXCEPTION_INIT (an_error); 
 
   /* Import status code into an existing, initialized, 
       address exception object */ 
 
   pthread_exc_set_status_np (&an_error, status_code); 
 

Note

On OpenVMS systems:

Threads Library exception status values are OpenVMS condition codes with a SEVERE severity level. If necessary, the pthread_exc_set_status_np() routine will modify the severity level of the status code to SEVERE.

5.8.3 Obtaining a System-Defined Error Status

In a program that uses status exceptions, use the
pthread_exc_get_status_np() routine to obtain the status value from a status exception object, such as after an exception is caught. If the routine's exception argument is a status exception object, it sets the status code argument and returns 0 (zero); otherwise, it returns [EINVAL] and does not set the status value argument.

Example 5-11 demonstrates using the pthread_exc_get_status_np() routine to obtain the status value associated with a caught status exception object.

Example 5-11 Obtaining the Error Status Value from a Status Exception Object

 
   #include <pthread_exception.h> 
   .
   .
   .
 
   TRY { 
      operation (); 
       } 
   CATCH_ALL { 
      unsigned long  status_code; 
 
      if (pthread_exc_get_status_np (THIS_CATCH, &status_code) == 0 
           && status_code == SOME_ERROR) 
        fprintf (stderr, "Exception %ld caught from system.\n", SOME_ERROR); 
      else 
        pthread_exc_report_np (THIS_CATCH); 
      } 
   ENDTRY 
 

5.8.4 Reporting a Caught Exception

Use the pthread_exc_report_np() routine to produce a message that reports what a given exception object represents. Your program calls this routine within a CATCH or CATCH_ALL code block to report on a caught exception.

An exception in your program that has not been handled by a CATCH or CATCH_ALL causes the unhandled exception handler to report the exception and immediately terminate the process. However, you might prefer to report a caught exception as part of your program's error recovery.

The pthread_exc_report_np() routine prints a message to stderr (on Tru64 UNIX systems) or SYS$ERROR (on OpenVMS systems) that describes the exception.

Each defined exception has an associated message that describes the given error condition. Typically, external status values can also be reported. When an address exception is reported, the Threads Library can only report the fact that an exception has occurred and the address of the exception object.

See Example 5-11 for an example using the pthread_exc_report_np() routine to report an error.

5.8.5 Determining Whether Two Exceptions Match

The pthread_exc_matches_np() routine compares two exception objects, taking into consideration whether each is an address exception or a status exception. Whenever you must compare two exception objects, use this routine.

Example 5-12 demonstrates how to use the pthread_exc_matches_np() routine to test for the equivalence of two exception objects.

Example 5-12 Comparing Two Exception Objects

 
   #include <pthread_exception.h> 
   .
   .
   .
   EXCEPTION my_status; 
 
   EXCEPTION_INIT (my_status); 
   pthread_exc_set_status_np (&my_status, status_code); 
   .
   .
   .
   TRY { 
      .
      .
      .
   } 
   .
   .
   .
   CATCH_ALL { 
      if (pthread_exc_matches_np (THIS_CATCH, &my_status)) 
         fprintf (stderr, "This is my exception\n"); 
      RERAISE; 
   } 
   ENDTRY 
   

5.9 Using Exceptions

This section presents guidelines for using exceptions in a modular way, so that independent software components can be written without requiring knowledge of each other, and includes tips on writing code using exceptions.

5.9.1 Develop Naming Conventions for Exceptions

Develop naming conventions for exception objects. A naming convention ensures that the names for exceptions that are declared extern in different modules do not conflict. The following convention is recommended:


   facility-prefix_error-name_e 

Example: pthread_cancel_e

5.9.2 Enclose Appropriate Actions in an Exception Scope

In a TRY code block avoid including code that more appropriately belongs outside it (in particular, before it). That is, the TRY macro should guard only operations for which there are appropriate handler operations in the scope's FINALLY , CATCH , or CATCH_ALL code blocks.

A common misuse of a TRY code block is to include code that should be executed before the TRY macro is invoked. Example 5-13 demonstrates this misuse.

Example 5-13 Incorrect Placement of Statements That Might Raise an Exception

 
   TRY { 
       handle = open_file (file_name); 
 
       /* Statements that might raise an exception here */ 
 
   } 
   FINALLY { 
       close (handle); 
   } 
   ENDTRY  
 

In this example, the FINALLY code block assumes that no exception is raised by calling the open_file() routine. If calling open_file() results in raising an exception, the FINALLY code block's close() operation will use an invalid identifier.

Thus, the code in Example 5-13 should be rewritten as shown in Example 5-14.

Example 5-14 Correct Placement of Statements That Might Raise an Exception

 
   handle = open_file (file_name); 
   TRY { 
 
       /* Statements that might raise an exception here */ 
 
   } 
   FINALLY { 
       close (handle); 
   } 
   ENDTRY 
 

Notice that the initialization code belongs prior to the invoking of the TRY macro, and the matching cleanup code belongs in the FINALLY code block. In this example, the open_file() call is moved to before the TRY macro, and the close() call is kept in the FINALLY block.

5.9.3 Raise Exceptions Prior to Performing Side-Effects

Raise exceptions prior to performing side-effects. That is, write routines that propagate exceptions to their callers, so that the routine does not modify any persistent process state before raising the exception. A matching close() call is required only if the open_file() operation is successful. (If an exception is raised, the caller cannot access the output parameters of the function, because the compiler may not have copied temporary values back to their home locations from registers.)

If the open_file() routine raises an exception, the identifier will not have been written, so this open operation must not require that a corresponding close() routine is called when open_file() raises an exception.

5.9.4 Exiting an Exception Scope

Do not place a return or goto statement between TRY and ENDTRY . It is invalid to return from, branch from, or leave by other means a TRY , CATCH , CATCH_ALL , or FINALLY block, such as by using a continue or break in an exception scope contained inside a loop or switch statement. After a given TRY macro is executed, the exceptions package requires that the corresponding ENDTRY macro is also executed unless an exception is raised or reraised.

5.9.5 Declare Variables Within Handler Code as Volatile

When declaring certain variables that are used within an exception scope, you must use the ANSI C volatile type attribute. The volatile attribute prevents the compiler from producing certain optimizations about such variables that would be unsafe if an exception were raised. This ensures that such a variable's value is reliable in an exception handler after an exception is raised.

Use the volatile type attribute for a variable whose value is written after the TRY macro is invoked and before the first CATCH/CATCH_ALL/FINALLY macro is invoked and whose value must be used when an exception is caught within a CATCH/CATCH_ALL/FINALLY block or (if the exception is caught and not reraised) after the ENDTRY macro is invoked.

Example 5-15 demonstrates the significance of using the volatile type qualifier for variables that are referenced within an exception scope.

Example 5-15 Use of the Volatile Type Qualifier Within an Exception Scope

 
   void demonstrate_volatile_in_exception_scope (void ) 
   { 
      int          updated_before_try; 
      int          updated; 
      static int   updated_static; 
      volatile int updated_volatile; 
 
      updated_before_try = 1; 
      updated = 2; 
      updated_static = 3; 
      updated_volatile = 4; 
 
      TRY { 
         updated = 6; 
         updated_static = 7; 
         updated_volatile = 8; 
 
         something_that_might_result_in_an_exception(); 
      } 
      CATCH (fully_handled_exception) { 
 
         /*  Fully handle the exception here. 
             Execute the code after ENDTRY next.  */ 
 
      } 
      CATCH_ALL {                 (1)
         if (updated > updated_static) 
            printf ("%d, %d", updated, updated_before_try); 
         if (updated > updated_volatile) 
            printf ("%d, %d", updated, updated_before_try); 
         RERAISE; 
      } 
      ENDTRY                      (2)
 
 
      /*     The following two statements use invalid 
             references to the variables updated and 
             updated_static.**                    */ 
 
      if (updated > updated_static) 
         printf ("%d, %d", updated, updated_before_try); 
      if (updated > updated_volatile) 
         printf ("%d, %d", updated, updated_before_try); 
 
   }      /* end demonstrate_volatile_in_exception_scope() */ 
 

  1. Values of updated_volatile and updated_before_try are reliable. Values of updated and updated_static are unreliable.
  2. Regardless of the path to this code, the values of updated_volatile and updated_before_try are reliable. If this code is reached after the ENDTRY macro is invoked and no exception has been raised, the values of updated and updated_static are reliable. If this code is reached after the exception fully_handled_exception has been caught, the values of updated and updated_static are unreliable.

The code in Example 5-15 demonstrates:

Test your program after compiling it with the "optimize" compiler option, to ensure that your program contains the appropriate exception handler code.

5.9.6 Reraise Caught Exceptions That Are Not Fully Handled

Reraise exceptions that are not fully handled. That is, reraise any exception that you catch, unless your handler has performed the complete recovery action for the error. This rule permits an unhandled exception to propagate to some final default handler that knows how to recover fully.

A corollary of this rule is that CATCH_ALL handlers must always reraise the exceptions they catch because they can catch any exception, including those not explicitly known to your code.

It is important to follow this convention, so that your program does not stop the propagation of a thread cancelation exception or thread-exit request exception. The Threads Library maps these requests into exceptions, so that exception handler code can have the opportunity to handle all exceptional conditions---from access violations to thread-exit. In some applications it is important to be able to catch these to preserve an external invariant, such as an on-disk database, but they must always be reraised so that the thread will terminate properly.

5.9.7 Avoid Dynamically Allocated Exception Objects

Avoid dynamically allocated exception objects. Local exception objects should be declared (explicitly or implicitly) as static , and extern exception objects are acceptable.

5.10 Exceptions Defined by the POSIX Threads Library

Table 5-1 lists the names of exception objects that are defined by the Threads Library and the meaning of each exception.

Exception object names that begin with the prefix pthread_ are raised within the runtime environment itself and are not meant to be raised by your program code. Names of exception objects that begin with pthread_exc_ are generic and belong to the exceptions package or represent exceptions raised by the underlying system.

Table 5-1 Names of Exception Objects Defined by the Threads Library
Exception Definition
pthread_cancel_e Thread cancelation in progress
pthread_exc_aritherr_e Unhandled floating-point exception signal ("arithmetic error")
pthread_exc_decovf_e Unhandled decimal overflow exception
pthread_exc_excpu_e "CPU-time limit exceeded"
pthread_exc_exfilsiz_e "File size limit exceeded"
pthread_exc_exquota_e Operation failed due to insufficient quota
pthread_exc_fltdiv_e Unhandled floating-point/decimal divide by zero exception
pthread_exc_fltovf_e Unhandled floating-point overflow exception
pthread_exc_fltund_e Unhandled floating-point underflow exception
pthread_exc_illaddr_e Data or object could not be referenced
pthread_exc_illinstr_e Unhandled illegal instruction signal ("illegal instruction")
pthread_exc_insfmem_e Insufficient virtual memory for requested operation
pthread_exc_intdiv_e Unhandled integer divide by zero exception
pthread_exc_intovf_e Unhandled integer overflow exception
pthread_exc_noexcmem_e Out of memory while processing an exception
pthread_exc_nopriv_e Insufficient privilege for requested operation
pthread_exc_privinst_e Unhandled privileged instruction fault exception
pthread_exc_resaddr_e Unhandled reserved addressing fault exception
pthread_exc_resoper_e Unhandled reserved operand fault exception
pthread_exc_SIGABRT_e Unhandled signal ABORT
pthread_exc_SIGBUS_e Unhandled bus error signal
pthread_exc_SIGEMT_e Unhandled EMT signal
pthread_exc_SIGFPE_e Unhandled floating-point exception signal
pthread_exc_SIGILL_e Unhandled illegal instruction signal
pthread_exc_SIGIOT_e Unhandled IOT signal
pthread_exc_SIGPIPE_e Unhandled broken pipe signal
pthread_exc_SIGSEGV_e Unhandled segmentation violation signal
pthread_exc_SIGSYS_e Unhandled bad system call signal
pthread_exc_SIGTRAP_e Unhandled trace or breakpoint trap signal
pthread_exc_subrng_e Unhandled subscript out of range exception
pthread_exc_uninitexc_e Uninitialized exception raised
pthread_exit_e Thread exiting using pthread_exit()
pthread_stackovf_e Attempted stack overflow was detected


Previous Next Contents Index