![]() |
Software > OpenVMS Systems > Documentation > 72final > 6493 ![]() HP OpenVMS Systems Documentation |
![]() |
Guide to DECthreads
Chapter 5
|
static EXCEPTION parity_error; /* Declare the exception */ EXCEPTION_INIT (parity_error); /* Initialize the exception */ |
Raise a DECthreads exception to indicate that your program has detected an error condition and in response to which the program must take some action. Your program raises the exception by invoking the DECthreads RAISE macro.
Example 5-1 demonstrates how to raise a DECthreads exception.
Example 5-1 Raising an Exception |
---|
int read_tape(void) { int ret; if (tape_is_ready) { static EXCEPTION parity_error; /* Declare it */ EXCEPTION_INIT (parity_error); /* Initialize it */ ret = read(tape_device); if (ret = BAD_PARITY) RAISE (parity_error); /* Raise it */ } } |
After your program raises a DECthreads exception, it is passed to a location within a block of code called an exception scope. The exception scope defines:
Example 5-2 shows a TRY code block with a CATCH code block defined to catch the exception object named parity_error when it is raised within the read_tape() routine.
Example 5-2 Catching an Exception Using CATCH |
---|
TRY { read_tape (); } CATCH (parity_error) { printf ("Oops, parity error, program terminating\n"); printf ("Try cleaning the heads!\n"); } ENDTRY |
Example 5-3 demonstrates how CATCH and CATCH_ALL code blocks work together to handle different raised exceptions within a given TRY code block.
Example 5-3 Catching an Exception Using CATCH and CATCH_ALL |
---|
int *local_mem; local_mem = malloc (sizeof (int)); TRY { /* An exception can be raised within this scope */ read_tape (); free (local_mem); } CATCH (parity_error) { printf ("Oops, parity error, program terminating\n"); printf ("Try cleaning the heads!\n"); free (local_mem); } CATCH_ALL { free (local_mem); RERAISE; } ENDTRY |
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 DECthreads default handler for unhandled exceptions receives the exception, produces a default error message that identifies the unhandled exception, then terminates the process.
Reraising is most appropriate for an exception caught in a CATCH_ALL block. Because this code block catches exceptions that are not "known" to your program's code, it is unlikely that your code is able to fully recover from the error condition that the exception represents.
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 without an exception being raised. This means that those actions need not be duplicated in your code.
Do not combine a FINALLY block with a CATCH block or CATCH_ALL block in the same TRY block.
This section describes the attributes of DECthreads exceptions (that is, the EXCEPTION type) and the behavior of the DECthreads exception package's exception handling macros (that is, RAISE and RERAISE, TRY, CATCH and CATCH_ALL, and FINALLY).
An exception 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.
The DECthreads exception package supports several operations on exceptions. Operations on exceptions allow a program to report and handle errors. If an exception is handled properly, the program can recover from certain errors. For example, if an exception is raised from a parity error while reading a tape, the recovery action might be to retry 100 times before giving up.
In the DECthreads exception package, an exception is a statically allocated variable of type EXCEPTION. Declaring and initializing an exception object documents that a program reports or handles a particular error.
The EXCEPTION type is designed to be an opaque type and should only be manipulated by the DECthreads exception package routines. The actual definition of the type may differ from one DECthreads release to another. The EXCEPTION type is defined in the pthread_exception.h header file.
In general, 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 DECthreads exception 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 declaring and initializing an exception object:
EXCEPTION parity_error; /* Declare it */ EXCEPTION_INIT (parity_error); /* Initialize it */ |
By default, when your program raises an exception object that has been properly initialized, only an address value is passed to the current exception scope. This form of DECthreads exception object is called an address exception, because the object's value is the address in your program where an error condition was detected. Your program code that handles address exceptions is fully portable among DECthreads-supported platforms.
Use address exceptions if the error conditions that can occur in your program are not assigned to a range of status codes. 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 uses the DECthreads exception package's pthread_exc_set_status_np() routine to assign a DIGITAL UNIX errno code (or OpenVMS condition code or Win32 status code) to the exception object. This form of DECthreads exception object is called a status 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.
When a facility called by DECthreads raises a system-level exception, DECthreads and its clients can catch the exception using a DECthreads status exception. Similarly, when a routine in your code raises a DECthreads exception, the calling routine might handle it using facilities provided by the language or platform.
Given two different exception objects that have been set with the same status value, the DECthreads exception package considers the two objects as functionally identical. For example, if one of the two exceptions is raised, it can be caught by specifying another exception object that has been set to the same status value. In contrast, DECthreads never considers two distinct address exception objects to be identical.
DECthreads 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 or CATCH_ALL 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 entering 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 exception's stack of scope handles the exception, DECthreads terminates the process, regardless of the state of the process's other threads. Termination prevents the unhandled error from affecting other areas of the process.
An exception scope serves two purposes:
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 defining an exception scope that encloses one operation, a call to the read_tape() routine.
Example 5-5 Defining an Exception Scope |
---|
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) { static EXCEPTION parity_error; /* Declare it */ 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 can be raised. That is, the block contains code that invokes, or directly or indirectly calls other code that invokes, the DECthreads exception package's RAISE macro when the program detects an error condition. Any exception raised within the block, or within any routines called directly or indirectly within the block, must 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.
After your program declares and initializes an exception object, your program raises that exception when it detects an error condition. Use the DECthreads exception package's RAISE macro to raise an exception.
Raising an exception 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:
If the exception scope within which an exception is raised does not define a handler or finalization block, then DECthreads simply "tears down" the current exception scope as the exception propagates up the DECthreads stack of exception scopes. This is also referred to as "unwinding" the stack.
Example 5-6 illustrates raising a DECthreads exception.
Example 5-6 Raising a DECthreads 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 */ } |
DECthreads exceptions are classified as terminating exceptions because after an exception is raised, 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 explicitly or implicitly "catches" that exception, or that defines an epilogue block for finalization processing. See Section 5.4.3 for further details.
Previous | Next | Contents | Index |