ATMI C Function Reference
TRY()
—Exception-returning interface.
#include <texc.h>
TRYtry_block
[ CATCH(exception_name
)
handler_block
] ...
[CATCH_ALLhandler_block
]
ENDTRY
TRYtry_block
FINALLYfinally_block
ENDTRY
RAISE(exception_name
)
RERAISE
/* declare exception */
EXCEPTIONexception_name
;
/* initialize address (application) exception */
EXCEPTION_INIT(EXCEPTIONexception_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
)
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_block
s.
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
.
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.
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.
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).
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.
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
}