SimGrid
3.10
Versatile Simulation of Distributed Systems
|
A set of macros providing exception a la C++ in ANSI C (grounding feature) More...
Data Structures | |
struct | xbt_ex_t |
Structure describing an exception. More... |
Macros | |
#define | TRY |
Introduce a block where exception may be dealed with. | |
#define | TRY_CLEANUP |
optional(!) block for cleanup | |
#define | CATCH(e) |
the block for catching (ie, deal with) an exception | |
#define | CATCH_ANONYMOUS |
like CATCH(e) but without argument | |
#define | THROW_PREPARE(_throw_ctx, c, v, m) |
Helper macro for THROW and THROWF. | |
#define | THROW(c, v) |
Builds and throws an exception. | |
#define | THROWF(c, v,...) |
Builds and throws an exception with a printf-like formatted message. | |
#define | RETHROW |
re-throwing of an already caught exception (ie, pass it to the upper catch block) | |
#define | RETHROWF(...) |
like THROWF, but adding some details to the message of an existing exception |
Enumerations | |
enum | xbt_errcat_t { unknown_error = 0, arg_error, bound_error, mismatch_error, not_found_error, system_error, network_error, timeout_error, cancel_error, thread_error, host_error, tracing_error, io_error } |
different kind of errors More... |
Functions | |
const char * | xbt_ex_catname (xbt_errcat_t cat) |
returns a short name for the given exception category | |
void | xbt_ex_free (xbt_ex_t e) |
Exception destructor. | |
void | xbt_backtrace_display_current (void) |
Shows a backtrace of the current location. | |
int | xbt_backtrace_no_malloc (void **bt, int size) |
reimplementation of glibc backtrace based directly on gcc library, without implicit malloc | |
void | xbt_backtrace_current (xbt_ex_t *e) |
Captures a backtrace for further use. | |
void | xbt_backtrace_display (xbt_ex_t *e) |
Display a previously captured backtrace. | |
int | xbt_libunwind_backtrace (void *bt[XBT_BACKTRACE_SIZE], int size) |
Get current backtrace with libunwind. |
A set of macros providing exception a la C++ in ANSI C (grounding feature)
This module is a small ISO-C++ style exception handling library for use in the ISO-C language. It allows you to use the paradigm of throwing and catching exceptions in order to reduce the amount of error handling code without hindering program robustness.
This is achieved by directly transferring exceptional return codes (and the program control flow) from the location where the exception is raised (throw point) to the location where it is handled (catch point) – usually from a deeply nested sub-routine to a parent routine. All intermediate routines no longer have to make sure that the exceptional return codes from sub-routines are correctly passed back to the parent.
These features are brought to you by a modified version of the libex library, one of the numerous masterpiece of Ralf S. Engelschall.
In SimGrid, an exception is a triple <msg , category , value> where msg is a human-readable text describing the exceptional condition, code an integer describing what went wrong and value providing a sort of sub-category. (this is different in the original libex).
TRY TRIED_BLOCK [TRY_CLEANUP CLEANUP_BLOCK] CATCH (variable) CATCH_BLOCK
This is the primary syntactical construct provided. It is modeled after the ISO-C++ try-catch clause and should sound familiar to most of you.
Any exception thrown directly from the TRIED_BLOCK block or from called subroutines is caught. Cleanups which must be done after this block (whenever an exception arose or not) should be placed into the optionnal CLEANUP_BLOCK. The code dealing with the exceptions when they arise should be placed into the (mandatory) CATCH_BLOCK.
In absence of exception, the control flow goes into the blocks TRIED_BLOCK and CLEANUP_BLOCK (if present); The CATCH_BLOCK block is then ignored.
When an exception is thrown, the control flow goes through the following blocks: TRIED_BLOCK (up to the statement throwing the exception), CLEANUP_BLOCK (if any) and CATCH_BLOCK. The exception is stored in a variable for inspection inside the CATCH_BLOCK. This variable must be declared in the outter scope, but its value is only valid within the CATCH_BLOCK block.
Some notes:
The TRY block is a regular ISO-C language statement block, but
This is because there is some hidden setup and cleanup that needs to be done regardless of whether an exception is caught. Bypassing these steps will break the exception handling facility. The symptom are likely to be a segfault at the next exception raising point, ie far away from the point where you did the mistake. If you suspect that kind of error in your code, have a look at the little script tools/xbt_exception_checker
in the CVS. It extracts all the TRY blocks from a set of C files you give it and display them (and only them) on the standard output. You can then grep for the forbidden keywords on that output.
The CLEANUP and CATCH blocks are regular ISO-C language statement blocks without any restrictions. You are even allowed to throw (and, in the CATCH block, to re-throw) exceptions.
There is one subtle detail you should remember about TRY blocks: Variables used in the CLEANUP or CATCH clauses must be declared with the storage class "volatile", otherwise they might contain outdated information if an exception is thrown.
This is because you usually do not know which commands in the TRY were already successful before the exception was thrown (logically speaking) and because the underlying ISO-C setjmp(3) facility applies those restrictions (technically speaking). As a matter of fact, value changes between the TRY and the THROW may be discarded if you forget the "volatile" keyword.
Exception handling is a very elegant and efficient way of dealing with exceptional situation. Nevertheless it requires additional discipline in programming and there are a few pitfalls one must be aware of. Look the following code which shows some pitfalls and contains many errors (assuming a mallocex() function which throws an exception if malloc(3) fails):
This example raises a few issues:
The following is fixed version of the code (annotated with the pitfall items for reference):
#define CATCH_ANONYMOUS |
like CATCH(e) but without argument
Useful if you only want to rethrow the exception caught, and do not want to bother with an unused variable.
#define THROW_PREPARE | ( | _throw_ctx, | |
c, | |||
v, | |||
m | |||
) |
Helper macro for THROW and THROWF.
_throw_ctx,: | the throwing context in which we should construct the exception |
c,: | category code (integer) |
v,: | value (integer) |
m,: | message text |
If called from within a TRY/CATCH construct, this exception is copied into the CATCH relevant variable program control flow is derouted to the CATCH (after the optional sg_cleanup).
If no TRY/CATCH construct embeds this call, the program calls abort(3).
The THROW can be performed everywhere, including inside TRY, CLEANUP and CATCH blocks.
enum xbt_errcat_t |
different kind of errors
int xbt_backtrace_no_malloc | ( | void ** | array, |
int | size | ||
) |
reimplementation of glibc backtrace based directly on gcc library, without implicit malloc
See http://webloria.loria.fr/~quinson/blog/2012/0208/system_programming_fun_in_SimGrid/ for the motivation behind this function