Skip navigation.

ATMI C Function Reference

  Previous Next vertical dots separating previous/next from contents/index/pdf Contents View as PDF   Get Adobe Reader

 


TRY(3c)

Name

TRY()—Exception-returning interface.

Synopsis

#include <texc.h>

TRY
try_block
[ CATCH(
exception_name) handler_block] ...
[CATCH_ALL
handler_block]
ENDTRY

TRY
try_block
FINALLY
finally_block
ENDTRY

RAISE(
exception_name)
RERAISE

/* declare exception */
EXCEPTION
exception_name;

/* initialize address (application) exception */
EXCEPTION_INIT(EXCEPTION
exception_name)

/* intialize status exception (map status to exception */
exc_set_status(EXCEPTION *
exception_name, long status)

/* map status exception to status */
exc_get_status(EXCEPTION *
exception_name, long *status)

/* compare exceptions */
exc_matches(EXCEPTION *
e1, EXCEPTION *e2)

/* print error to stderr */
void exc_report(EXCEPTION *
exception)

Description

The TRY/CATCH interface provides a mechanism to handle exceptions without the use of status variables (for example, errno or status variables passed back from an RPC operation). These macros are defined in texc.h.

The TRY try_block is a block of C or C++ declarations and statements in which an exception may be raised (code that is not associated with raising an exception should be placed before or after the try_block). Each TRY/ENDTRY pair constitutes a "scope," with respect to exceptions (not unlike C scoping), or a region of code over which exceptions are caught. These scopes can be properly nested. When an exception is raised, an error is reported to the application by searching the active scopes for actions written to handle ("absorb") an exception (CATCH or CATCH_ALL clauses) or complete the scopes (FINALLY clauses). If a scope does not handle an exception, the scope is torn down with the exception raised at the next higher level (unwinding the stack of exception scopes). Execution resumes at the point after which the exception is handled; there is no provision for resuming execution at the point of error. If the exception is not handled by any scope, the program is terminated (a message is written to the log via userlog(3c) and abort(3) is called).

Zero or more occurrences of CATCH (exception_name) handler_block may be provided. Each handler_block is a block of C or C++ declarations and statements in which the associated exception (exception_name) is processed (normally, actions are specified for recovery from the failure). If an exception is raised by a statement in try_block, then the first CATCH clause that matches the exception is executed.

Within a CATCH or CATCH_ALL handler_block, the current exception can be referenced by the EXCEPTION pointer THIS_CATCH (for example, for logic based on or printing the exception value).

If the exception is not handled by one of the CATCH clauses, then the CATCH_ALL clause is executed. By default, no further action is taken for an exception that is handled by a CATCH or CATCH_ALL clause. If no CATCH_ALL clause exists, then the exception is raised at the try_block at the next higher level, assuming that the try_block is nested within another try_block. If an ANSI C compiler is used, register and automatic variables that are used in the handler blocks should be declared with the volatile attribute (as is true of any blocks that use setjmp/longjmp). Also note that output parameters and return values from the functions that can generate an exception are indeterminate.

Within a CATCH or CATCH_ALL handler_block, the current exception can be propagated to the next higher level (the exception is "reraised") using the RERAISE statement. The RERAISE statement must appear lexically within the scope of a handler_block (that is, not within a function called by the handler_block). Any exception that is caught but not fully handled should be reraised. In many cases, a CATCH_ALL handler should reraise the exception because the handler is not written to handle every exception. The application should also be written such that an exception is raised to the proper scope such that the handler blocks take the appropriate actions and modify the appropriate state (for example, if an exception occurs while opening a file, the handler function for that level should not try to close the unopened file).

An exception can be raised from anywhere by using the RAISE(exception_name) statement. This statement causes the exception to start propagating at the current try_block and will be reraised until it is handled.

The FINALLY clause can be used to specify an epilogue block of code that is executed after the try_block, whether or not an exception is raised. If an exception is raised in the try_block, it is reraised after the finally_block is executed. This clause can be used to avoid replicating epilogue code twice, once in a CATCH_ALL clause, and again after the ENDTRY. It is normally used to execute cleanup activities, restoring invariants (for example, shared data, locks) as the scopes are unwound, whether or not exceptions are raised (that is, on both normal and abnormal exits from the block). Note (in the "Synopsis" section) that a FINALLY clause cannot be used with a CATCH or CATCH_ALL clause for the same try_block; use nested try_blocks.

The ENDTRY statement must be used to complete the TRY block, since it contains code that must be executed to make sure that exceptions are handled and the context is cleaned up. A try_block, handler_block, or finally_block must not contain a return, non-local jump, or any other means of leaving the block such that the ENDTRY is not reached (for example, goto(), break(), continue(), longjmp()).

This interface is provided to handle exceptions from RPC operations. However, this is a generic interface that can be used for any application. An exception is declared to be of type EXCEPTION. (This is a complex data type; do not try to use it like a long integer.) There are two types of exceptions. They are declared in the same manner but initialized differently.

One type of exception is used to define application exceptions. It is initialized by calling the EXCEPTION_INIT() macro. The address of the exception is stored as the value within the address exception. Note that this value is valid only within a single address space and will change if the exception is an automatic variable. For this reason, an address exception should be declared as a static or external variable, not an automatic or register variable. The exc_get_status() macro will evaluate to -1 for an address exception. Using the exc_set_status() macro on this exception will make it a status exception.

The exc_matches macro can be used to compare two exceptions. To compare equal, the exceptions must both be the same type and have the same value (for example, the same status value for status exceptions, or the same addresses for address exceptions). This comparison is used for the CATCH clause, described above.

When status exceptions are raised, a common part of handling the exception might be to print out the status value, or better yet, a string indicating what status value occurred. If the string is to be printed to the standard error output, then the function exc_report() can be called with a pointer to the status exception to print the string in one operation.

CATCH_ALL 
{
exc_report(THIS_CATCH);
}
ENDTRY

If something else is to be done with the string (for example, printing the error to the user log), exc_get_status() can be used on THIS_CATCH to get the status value (remember that THIS_CATCH is already a pointer to an EXCEPTION, not an EXCEPTION), and dce_error_inq_text() can be used to get the string value associated with the status value.

CATCH_ALL 
{
unsigned long status_to_convert;
unsigned char error_text[200];
int status;

exc_get_status(THIS_CATCH,status_to_convert);
dce_error_inq_text(status_to_convert, error_text, status);
userlog("%s", (char *)error_text);
}
ENDTRY

Note: A thread in a multithreaded application may invoke the TRY/CATCH interface while running in any context state, including TPINVALIDCONTEXT.

When to Use Exception and Status Returns

The status of RPC operations can be determined portably by defining status variables for each operation ([comm_status] and [fault_status] parameters are defined via the Attribute Configuration File). The status-returning interface is the only interface provided in the X/OPEN RPC specification. The fault_status attribute indicates that errors occurring on the server due to incorrectly specified parameter values, resource constraints, or coding errors be reported by an additional status argument or return value. Similarly, the comm_status attribute indicates that RPC communications failures be reported by an additional status argument or return value. Using status values works well for fine-grained error handling (on a per-call basis) with recovery specified for each possible error on each call, and where it is necessary to retry from the point of failure. The disadvantage is that it is not transparent whether or not the call is local or remote. The remote call has additional status parameters, or a status return value instead of being a void return. Thus, the application must have procedure declarations adjusted between local and distributed code.

For application portability from an OSF/DCE environment, the TRY/CATCH exception-returning interface is also provided. This interface may not be provided in all environments. However, it has the advantage that procedure declarations need not be adjusted between local and distributed code, maintaining existing interfaces. The checking for errors can be simplified such that each procedure call does not have specific failure checking or recovery code. If an error is not handled at some level, then the program exits with a system error message such that the error is detected and can be corrected (omissions become more obvious). Exceptions work better for coarse-grained exception handling.

Built-in Exceptions

The exceptions shown in Table 12 are "built-in" to the use of this exception interface. The first TRY clause sets up a signal handler to catch the signals list below if they are not currently ignored or caught; the other exceptions are defined only for DCE program portability.

Table 12 Built-in Exceptions 

Exception

Description

exc_e_SIGBUS

An unhandled SIGBUS signal occurred.

exc_e_SIGEMT

An unhandled SIGEMT signal occurred.

exc_e_SIGFPE

An unhandled SIGFPE signal occurred.

exc_e_SIGILL

An unhandled SIGILL signal occurred.

exc_e_SIGIOT

An unhandled SIGIOT signal occurred.

exc_e_SIGPIPE

An unhandled SIGPIPE signal occurred.

exc_e_SIGSEGV

An unhandled SIGSEGV signal occurred.

exc_e_SIGSYS

An unhandled SIGSYS signal occurred.

exc_e_SIGTRAP

An unhandled SIGTRAP signal occurred.

exc_e_SIGXCPU

An unhandled SIGXCPU signal occurred.

exc_e_SIGXFSZ

An unhandled SIGXFSZ signal occurred.

pthread_e_badparam


pthread_e_defer_q_full


pthread_e_existence


pthread_e_in_use


pthread_e_nostackmem


pthread_e_nostack


pthread_e_signal_q_full


pthread_e_stackovf


pthread_e_unimp


pthread_e_use_error


exc_e_decovf


exc_e_exquota


exc_e_fltdiv


exc_e_fltovf


exc_e_fltund


exc_e_illaddr


exc_e_insfmem


exc_e_intdiv


exc_e_intovf


exc_e_nopriv


exc_e_privinst


exc_e_resaddr


exc_e_resoper


exc_e_subrng


exc_e_uninitexc



 

These same exception codes are also defined with the "_e" at the end of the name (for example, exc_e_SIGBUS is also defined as exc_SIGBUS_e). Equivalent status codes are defined with similar names but the "_e_" is changed to "_s_" (for example, exc_e_SIGBUS is equivalent to the exc_s_SIGBUS status code).

Caveats

In OSF/DCE, the header file is named exc_handling.h; the BEA Tuxedo ATMI system header file is texc.h. It is not possible for the same source file to use both DCE and BEA Tuxedo ATMI system exception handling. Further, within a program, the handling of signal exceptions can only be done by either DCE or the BEA Tuxedo ATMI system, not both.

Examples

The following is an example C source file that uses exceptions:

#include <texc.h>  

EXCEPTION badopen_e; /* declare exception for bad open() */

doit(char *filename)
{
EXCEPTION_INIT(badopen_e); /* initialize exception */
TRY get_and_update_data(filename); /* do the operation */
CATCH(badopen_e) /* exception - open() failed */
fprintf(stderr, "Cannot open %s\en", filename);
CATCH_ALL /* handle other errors */
/* handle rpc service not available, ... */
exc_report(THIS_CATCH)
ENDTRY
}
/*
* Open output file
* Get the remote data item
* Write out to file
*/
get_and_update_data(char *filename)
{
FILE *fp;
if ((fp == fopen(filename)) == NULL) /* open output file */
RAISE(badopen_e); /* raise exception */
TRY
/* in this block, file is opened successfully -
* use associated FINALLY to close file
*/
long data;
/*
* Execute RPC call - exceptions are raised to the calling
* function, doit()
*/
data = remote_get_data();
fprintf(fp, "%ld\en", data);
FINALLY
/* Whether or not exceptions are raised, close the file */
fclose(fp);
ENDTRY
}

See Also

userlog(3c)

abort(2) in a UNIX system reference manual

Programming BEA Tuxedo ATMI Applications Using TxRPC

 

Skip navigation bar  Back to Top Previous Next