SimGrid  3.13
Versatile Simulation of Distributed Systems
Exception support

A set of macros providing exception a la C++ in ANSI C (grounding feature) More...

Classes

struct  xbt_ex_t
 Structure describing an exception. More...
 
struct  xbt_running_ctx_t
 

Macros

#define XBT_RUNNING_CTX_INITIALIZER
 
#define XBT_RUNNING_CTX_INITIALIZE(ctx)
 
#define TRY
 Introduce a block where exception may be dealed with. More...
 
#define TRY_CLEANUP
 optional(!) block for cleanup More...
 
#define XBT_EX_T_CPLUSPLUSCAST
 
#define CATCH(e)
 the block for catching (ie, deal with) an exception More...
 
#define CATCH_ANONYMOUS
 like CATCH(e) but without argument More...
 
#define DO_CATCH(_xbt_do_catch_set_e)
 
#define DO_THROW(running_ctx)
 
#define THROW_PREPARE(_throw_ctx, c, v, m)
 Helper macro for THROW and THROWF. More...
 
#define _XBT_THROW(c, v, m)
 
#define THROW(c, v)
 Builds and throws an exception. More...
 
#define THROWF(c, v, ...)
 Builds and throws an exception with a printf-like formatted message. More...
 
#define THROW_IMPOSSIBLE   THROWF(unknown_error, 0, "The Impossible Did Happen (yet again)")
 
#define THROW_UNIMPLEMENTED   THROWF(unknown_error, 0, "Function %s unimplemented",__func__)
 
#define THROW_DEADCODE   THROWF(unknown_error, 0, "Function %s was supposed to be DEADCODE, but it's not",__func__)
 
#define DIE_IMPOSSIBLE   xbt_die("The Impossible Did Happen (yet again)")
 
#define RETHROW
 re-throwing of an already caught exception (ie, pass it to the upper catch block) More...
 
#define RETHROWF(...)
 like THROWF, but adding some details to the message of an existing exception More...
 

Typedefs

typedef struct xbt_running_ctx_t xbt_running_ctx_t
 
typedef xbt_running_ctx_t *(* xbt_running_ctx_fetcher_t) (void)
 
typedef void(* ex_term_cb_t) (xbt_ex_t *)
 

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, vm_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 More...
 
xbt_running_ctx_t__xbt_ex_ctx_default (void)
 
void __xbt_ex_terminate_default (xbt_ex_t *e)
 
void xbt_ex_free (xbt_ex_t e)
 Exception destructor. More...
 
void xbt_ex_display (xbt_ex_t *e)
 The display made by an exception that is not catched. More...
 
void xbt_backtrace_display_current (void)
 Shows a backtrace of the current location. More...
 
int xbt_backtrace_no_malloc (void **bt, int size)
 reimplementation of glibc backtrace based directly on gcc library, without implicit malloc More...
 
void xbt_backtrace_current (xbt_ex_t *e)
 Captures a backtrace for further use. More...
 
void xbt_backtrace_display (xbt_ex_t *e)
 Display a previously captured backtrace. More...
 
int xbt_libunwind_backtrace (void *bt[XBT_BACKTRACE_SIZE], int size)
 Get current backtrace with libunwind. More...
 

Variables

const xbt_running_ctx_t __xbt_ex_ctx_initializer
 
xbt_running_ctx_fetcher_t __xbt_running_ctx_fetch
 
ex_term_cb_t __xbt_ex_terminate
 

Detailed Description

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.

DESCRIPTION

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).

BASIC USAGE

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

it is not allowed to jump into it via "goto" or longjmp(3) or out of it via "break", "return", "goto" or longjmp(3).

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. Finding the problem can reveal challenging, unfortunately.

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.

PROGRAMMING PITFALLS

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:

  1. variable scope
    Variables which are used in the CLEANUP or CATCH clauses must be declared before the TRY clause, otherwise they only exist inside the TRY block. In the example above, cp1, cp2 and cp3 only exist in the TRY block and are invisible from the CLEANUP and CATCH blocks.
  2. variable initialization
    Variables which are used in the CLEANUP or CATCH clauses must be initialized before the point of the first possible THROW is reached. In the example above, CLEANUP would have trouble using cp3 if mallocex() throws a exception when allocating a TOOBIG buffer.
  3. volatile variable
    Variables which are used in the CLEANUP or CATCH clauses MUST BE DECLARED AS "volatile", otherwise they might contain outdated information when an exception is thrown.
  4. clean before catch
    The CLEANUP clause is not only place before the CATCH clause in the source code, it also occures before in the control flow. So, resources being cleaned up cannot be used in the CATCH block. In the example, c3 gets freed before the printf placed in CATCH.
  5. variable uninitialization
    If resources are passed out of the scope of the TRY/CLEANUP/CATCH construct, they naturally shouldn't get cleaned up. The example above does free(3) cp1 in CLEANUP although its value was affected to globalcontext->first, invalidating this pointer.

The following is fixed version of the code (annotated with the pitfall items for reference):

Macro Definition Documentation

#define XBT_RUNNING_CTX_INITIALIZER
Value:
{ NULL, 0, { /* content */ NULL, unknown_error, 0, \
/* throw point*/ NULL, 0, NULL, 0, NULL, \
/* backtrace */ 0, NULL, /* bt[] */ } }
unknown error
Definition: ex.h:251
#define XBT_RUNNING_CTX_INITIALIZE (   ctx)
Value:
(ctx)->ctx_mctx = NULL; \
(ctx)->ctx_caught = 0; \
(ctx)->exception.msg = NULL; \
(ctx)->exception.category = unknown_error; \
(ctx)->exception.value = 0; \
(ctx)->exception.procname = NULL; \
(ctx)->exception.pid = 0; \
(ctx)->exception.file = NULL; \
(ctx)->exception.line = 0; \
(ctx)->exception.used = 0; \
(ctx)->exception.bt_strings = NULL;
unknown error
Definition: ex.h:251
#define TRY

Introduce a block where exception may be dealed with.

#define TRY_CLEANUP

optional(!) block for cleanup

#define XBT_EX_T_CPLUSPLUSCAST
#define CATCH (   e)

the block for catching (ie, deal with) an exception

#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 DO_CATCH (   _xbt_do_catch_set_e)
Value:
else { \
} \
if (!(__ex_cleanup)) \
__xbt_ex_ctx_ptr->ctx_caught = 0; \
} else { \
if (!(__ex_cleanup)) { \
__ex_mctx_restored(&__ex_mctx_me); \
__xbt_ex_ctx_ptr->ctx_caught = 1; \
} \
} \
__xbt_ex_ctx_ptr->ctx_mctx = __ex_mctx_en; \
} \
if ( !(__xbt_running_ctx_fetch()->ctx_caught) \
|| ((void)(_xbt_do_catch_set_e), \
MAYDAY_CATCH(__xbt_running_ctx_fetch()->exception) 0)) { \
} \
else
xbt_running_ctx_fetcher_t __xbt_running_ctx_fetch
Definition: ex.c:189
#define MAYDAY_CATCH(e)
Definition: ex.h:74
#define __ex_mctx_restored(mctx)
Definition: ex.h:93
if(!(yy_init))
Definition: dax_dtd.c:1795
#define DO_THROW (   running_ctx)
Value:
do { /* deal with the exception */ \
xbt_running_ctx_t *ctx = (running_ctx); \
if (ctx->ctx_mctx == NULL) \
__xbt_ex_terminate((xbt_ex_t*)&(ctx->exception)); /* not catched */ \
__ex_mctx_restore(ctx->ctx_mctx); /* catched somewhere */ \
abort(); /* nope, stupid GCC, we won't survive a THROW */ \
/* (this won't be reached) */ \
} while(0)
#define __ex_mctx_restore(mctx)
Definition: ex.h:94
if(!(yy_init))
Definition: dax_dtd.c:1795
Structure describing an exception.
Definition: ex.h:270
ex_term_cb_t __xbt_ex_terminate
Definition: ex.c:190
struct xbt_running_ctx_t xbt_running_ctx_t
#define THROW_PREPARE (   _throw_ctx,
  c,
  v,
 
)

Helper macro for THROW and THROWF.

Parameters
_throw_ctxthe throwing context in which we should construct the exception
ccategory code (integer)
vvalue (integer)
mmessage 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.

#define _XBT_THROW (   c,
  v,
 
)
Value:
do { /* change this sequence into one block */ \
THROW_PREPARE(_throw_ctx, c, v, m); \
DO_THROW(_throw_ctx); \
} while (0)
xbt_running_ctx_fetcher_t __xbt_running_ctx_fetch
Definition: ex.c:189
#define THROW_PREPARE(_throw_ctx, c, v, m)
Helper macro for THROW and THROWF.
Definition: ex.h:429
#define DO_THROW(running_ctx)
Definition: ex.h:399
struct xbt_running_ctx_t xbt_running_ctx_t
#define THROW (   c,
 
)

Builds and throws an exception.

#define THROWF (   c,
  v,
  ... 
)

Builds and throws an exception with a printf-like formatted message.

#define THROW_IMPOSSIBLE   THROWF(unknown_error, 0, "The Impossible Did Happen (yet again)")
#define THROW_UNIMPLEMENTED   THROWF(unknown_error, 0, "Function %s unimplemented",__func__)
#define THROW_DEADCODE   THROWF(unknown_error, 0, "Function %s was supposed to be DEADCODE, but it's not",__func__)
#define DIE_IMPOSSIBLE   xbt_die("The Impossible Did Happen (yet again)")
#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

Typedef Documentation

typedef xbt_running_ctx_t*(* xbt_running_ctx_fetcher_t) (void)
typedef void(* ex_term_cb_t) (xbt_ex_t *)

Enumeration Type Documentation

different kind of errors

Enumerator
unknown_error 

unknown error

arg_error 

Invalid argument.

bound_error 

Out of bounds argument.

mismatch_error 

The provided ID does not match.

not_found_error 

The searched element was not found.

system_error 

a syscall did fail

network_error 

error while sending/receiving data

timeout_error 

not quick enough, dude

cancel_error 

an action was canceled

thread_error 

error while [un]locking

host_error 

host failed

tracing_error 

error during the simulation tracing

io_error 

disk or file error

vm_error 

vm error

Function Documentation

const char* xbt_ex_catname ( xbt_errcat_t  cat)

returns a short name for the given exception category

xbt_running_ctx_t* __xbt_ex_ctx_default ( void  )
void __xbt_ex_terminate_default ( xbt_ex_t e)
void xbt_ex_free ( xbt_ex_t  e)

Exception destructor.

void xbt_ex_display ( xbt_ex_t e)

The display made by an exception that is not catched.

The display made by an exception that is not catched.

void xbt_backtrace_display_current ( void  )

Shows a backtrace of the current location.

Shows a backtrace of the current location.

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

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.

Variable Documentation

const xbt_running_ctx_t __xbt_ex_ctx_initializer
xbt_running_ctx_fetcher_t __xbt_running_ctx_fetch
ex_term_cb_t __xbt_ex_terminate