Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
s/sg/xbt/ ; s/code/category/; document this stuff
[simgrid.git] / include / xbt / ex.h
1 /*
2 **  OSSP ex - Exception Handling (modified to fit into SimGrid)
3 **  Copyright (c) 2005 Martin Quinson.
4 **  Copyright (c) 2002-2004 Ralf S. Engelschall <rse@engelschall.com>
5 **  Copyright (c) 2002-2004 The OSSP Project <http://www.ossp.org/>
6 **  Copyright (c) 2002-2004 Cable & Wireless <http://www.cw.com/>
7 **
8 **  This file is part of OSSP ex, an exception handling library
9 **  which can be found at http://www.ossp.org/pkg/lib/ex/.
10 **
11 **  Permission to use, copy, modify, and distribute this software for
12 **  any purpose with or without fee is hereby granted, provided that
13 **  the above copyright notice and this permission notice appear in all
14 **  copies.
15 **
16 **  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
17 **  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 **  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 **  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
20 **  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 **  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 **  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 **  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 **  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 **  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26 **  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 **  SUCH DAMAGE.
28 **
29 **  ex.h: exception handling (pre-processor part)
30 */
31
32 #ifndef __XBT_EX_H__
33 #define __XBT_EX_H__
34
35 #include <xbt/misc.h>
36 #include <xbt/sysdep.h>
37
38 /* required ISO-C standard facilities */
39 #include <stdio.h>
40
41 /* the machine context */
42 #if defined(__EX_MCTX_MCSC__)
43 #include <ucontext.h>            /* POSIX.1 ucontext(3) */
44 #define __ex_mctx_struct         ucontext_t uc;
45 #define __ex_mctx_save(mctx)     (getcontext(&(mctx)->uc) == 0)
46 #define __ex_mctx_restored(mctx) /* noop */
47 #define __ex_mctx_restore(mctx)  (void)setcontext(&(mctx)->uc)
48
49 #elif defined(__EX_MCTX_SSJLJ__)
50 #include <setjmp.h>              /* POSIX.1 sigjmp_buf(3) */
51 #define __ex_mctx_struct         sigjmp_buf jb;
52 #define __ex_mctx_save(mctx)     (sigsetjmp((mctx)->jb, 1) == 0)
53 #define __ex_mctx_restored(mctx) /* noop */
54 #define __ex_mctx_restore(mctx)  (void)siglongjmp((mctx)->jb, 1)
55
56 #elif defined(__EX_MCTX_SJLJ__) || !defined(__EX_MCTX_CUSTOM__)
57 #include <setjmp.h>              /* ISO-C jmp_buf(3) */
58 #define __ex_mctx_struct         jmp_buf jb;
59 #define __ex_mctx_save(mctx)     (setjmp((mctx)->jb) == 0)
60 #define __ex_mctx_restored(mctx) /* noop */
61 #define __ex_mctx_restore(mctx)  (void)longjmp((mctx)->jb, 1)
62 #endif
63
64 /* declare the machine context type */
65 typedef struct { __ex_mctx_struct } __ex_mctx_t;
66
67 /** \ingroup XBT_ex
68  *
69  * This module is a small ISO-C++ style exception handling library
70  * for use in the ISO-C language. It allows you to use the paradigm 
71  * of throwing and catching exceptions in order to reduce the amount
72  * of error handling code without hindering program robustness.
73  *               
74  * This is achieved by directly transferring exceptional return codes
75  * (and the program control flow) from the location where the exception
76  * is raised (throw point) to the location where it is handled (catch
77  * point) -- usually from a deeply nested sub-routine to a parent 
78  * routine. All intermediate routines no longer have to make sure that 
79  * the exceptional return codes from sub-routines are correctly passed 
80  * back to the parent.
81  *
82  * These features are brought to you by a modified version of the libex 
83  * library, one of the numerous masterpiece of Ralf S. Engelschall.
84  *
85  * @section Introduction
86  * 
87  * In SimGrid, exceptions is a triple <\a msg , \a category , \a value> 
88  * where \a msg is a human-readable text describing the exceptional 
89  * condition, \a code an integer describing what went wrong and \a value
90  * providing a sort of sub-category. (this is different in the original libex).
91  *
92  * @section Basic usage
93  *
94  * \em xbt_try \b TRIED_BLOCK [\em xbt_cleanup \b CLEANUP_BLOCK] \em xbt_catch (variable) \b CATCH_BLOCK
95  *
96  * This is the primary syntactical construct provided. It is modeled after the
97  * ISO-C++ try-catch clause and should sound familiar to most of you.
98  *
99  * Any exception thrown directly from the TRIED_BLOCK block or from called
100  * subroutines is caught. Cleanups which must be done after this block
101  * (whenever an exception arised or not) should be placed into the optionnal
102  * CLEANUP_BLOCK. The code dealing with the exceptions when they arise should
103  * be placed into the (mandatory) CATCH_BLOCK.
104  *
105  * 
106  * In absence of exception, the control flow goes into the blocks TRIED_BLOCK
107  * and CLEANUP_BLOCK (if present); The CATCH_BLOCK block is then ignored. 
108  *
109  * When an exception is thrown, the control flow goes through the following
110  * blocks: TRIED_BLOCK (up to the statement throwing the exception),
111  * CLEANUP_BLOCK (if any) and CATCH_BLOCK. The exception is stored in a
112  * variable for inspection inside the CATCH_BLOCK. This variable must be
113  * declared in the outter scope, but its value is only valid within the
114  * CATCH_BLOCK block. 
115  *
116  * Some notes:
117  *  - xbt_try, xbt_cleanup and xbt_catch cannot be used separately, they work
118  *    only in combination and form a language clause as a whole.
119  *  - In contrast to the syntax of other languages (such as C++ or Jave) there
120  *    is only one xbt_catch block and not multiple ones (all exceptions are
121  *    of the same C type xbt_t). 
122  *  - the variable of xbt_catch can naturally be reused in subsequent 
123  *    xbt_catch clauses.
124  *  - it is possible to nest xbt_try clauses.
125  *
126  * The xbt_try block is a regular ISO-C language statement block, but it is not
127  * allowed to jump into it via "goto" or longjmp(3) or out of it via "break",
128  * "return", "goto" or longjmp(3) because there is some hidden setup and
129  * cleanup that needs to be done regardless of whether an exception is
130  * caught. Bypassing these steps will break the exception handling facility.
131  *     
132  * The xbt_cleanup and xbt_catch blocks are regular ISO-C language statement
133  * blocks without any restrictions. You are even allowed to throw (and in the
134  * xbt_catch block to re-throw) exceptions.
135  *
136  * There is one subtle detail you should remember about xbt_try blocks:
137  * Variables used in the xbt_cleanup or xbt_catch clauses must be declared with
138  * the storage class "volatile", otherwise they might contain outdated
139  * information if an exception it thrown.
140  *
141  *
142  * This is because you usually do not know which commands in the xbt_try
143  * were already successful before the exception was thrown (logically speaking)
144  * and because the underlying ISO-C setjmp(3) facility applies those
145  * restrictions (technically speaking). As a matter of fact, value changes
146  * between the xbt_try and the xbt_throw may be discarded if you forget the
147  * "volatile" keyword. 
148  * 
149  * @section Advanced usage
150  *
151  * @subsection xbt_defer DEFERING_BLOCK
152  *
153  * This directive executes DEFERING_BLOCK while deferring the throwing of
154  * exceptions, i.e., exceptions thrown within this block are remembered, but
155  * the control flow still continues until the end of the block. At its end, the
156  * first exception which occured within the block (if any) is rethrown (any
157  * subsequent exceptions are ignored).
158  *
159  * DEFERING_BLOCK is a regular ISO-C language statement block, but it is not
160  * allowed to jump into it via "goto" or longjmp(3) or out of it via "break",
161  * "return", "goto" or longjmp(3). It is however allowed to nest xbt_defer
162  * clauses.
163  *
164  * @subsection xbt_shield SHIELDED_BLOCK
165  *
166  * This directive executes SHIELDED_BLOCK while shielding it against the
167  * throwing of exceptions, i.e., any exception thrown from this block or its
168  * subroutines are silently ignored.
169  *
170  * SHIELDED_BLOCK is a regular ISO-C language statement block, but it is not
171  * allowed to jump into it via "goto" or longjmp(3) or out of it via "break",
172  * "return", "goto" or longjmp(3).  It is however allowed to nest xbt_shield
173  * clauses.
174  *
175  * @subsection Retrieving the current execution condition
176  *
177  * \a xbt_catching, \a xbt_deferred and \a xbt_shielding return a boolean
178  * indicating whether the current scope is within a TRYIED_BLOCK,
179  * DEFERING_BLOCK and SHIELDED_BLOCK (respectively)
180  *
181  * \section PROGRAMMING PITFALLS
182  *
183  * Exception handling is a very elegant and efficient way of dealing with
184  * exceptional situation. Nevertheless it requires additional discipline in
185  * programming and there are a few pitfalls one must be aware of. Look the
186  * following code which shows some pitfalls and contains many errors (assuming
187  * a mallocex() function which throws an exception if malloc(3) fails):
188  *
189  \verbatim
190 // BAD EXAMPLE
191 xbt_try {
192   char *cp1, *cp2, cp3;
193
194   cp1 = mallocex(SMALLAMOUNT);
195   globalcontext->first = cp1;
196   cp2 = mallocex(TOOBIG);
197   cp3 = mallocex(SMALLAMOUNT);
198   strcpy(cp1, "foo");
199   strcpy(cp2, "bar");
200 } xbt_cleanup {
201   if (cp3 != NULL) free(cp3);
202   if (cp2 != NULL) free(cp2);
203   if (cp1 != NULL) free(cp1);
204 } xbt_catch(ex) {
205   printf("cp3=%s", cp3);
206   ex_rethrow;
207 }\endverbatim
208
209  * This example raises a few issues:
210  *  -# \b variable scope\n
211  *     Variables which are used in the xbt_cleanup or xbt_catch clauses must be
212  *     declared before the xbt_try clause, otherwise they only exist inside the
213  *     xbt_try block. In the example above, cp1, cp2 and cp3 only exist in the
214  *     xbt_try block and are invisible from the xbt_cleanup and xbt_catch
215  *     blocks.
216  *  -# \b variable initialization \n
217  *     Variables which are used in the xbt_cleanup or xbt_catch clauses must
218  *     be initialized before the point of the first possible xbt_throw is
219  *     reached. In the example above, xbt_cleanup would have trouble using cp3
220  *     if mallocex() throws a exception when allocating a TOOBIG buffer.
221  *  -# \b volatile variable \n
222  *     Variables which are used in the xbt_cleanup or xbt_catch clauses MUST BE
223  *     DECLARED AS "volatile", otherwise they might contain outdated
224  *     information when an exception is thrown. 
225  *  -# \b clean before catch \n
226  *     The xbt_cleanup clause is not only place before the xbt_catch clause in
227  *     the source code, it also occures before in the control flow. So,
228  *     resources being cleaned up cannot be used in the xbt_catch block. In the
229  *     example, c3 gets freed before the printf placed in xbt_catch.
230  *  -# \b variable uninitialization \n
231  *     If resources are passed out of the scope of the
232  *     xbt_try/xbt_cleanup/xbt_catch construct, they naturally shouldn't get
233  *     cleaned up. The example above does free(3) cp1 in xbt_cleanup although
234  *     its value was affected to globalcontext->first, invalidating this
235  *     pointer.
236
237  * The following is fixed version of the code (annotated with the pitfall items
238  * for reference):
239  \verbatim
240 // GOOD EXAMPLE
241 { / *01* /
242   char * volatile / *03* / cp1 = NULL / *02* /;
243   char * volatile / *03* / cp2 = NULL / *02* /;
244   char * volatile / *03* / cp3 = NULL / *02* /;
245   try {
246     cp1 = mallocex(SMALLAMOUNT);
247     globalcontext->first = cp1;
248     cp1 = NULL / *05 give away* /;
249     cp2 = mallocex(TOOBIG);
250     cp3 = mallocex(SMALLAMOUNT);
251     strcpy(cp1, "foo");
252     strcpy(cp2, "bar");
253   }
254   clean { / *04* /
255     printf("cp3=%s", cp3 == NULL / *02* / ? "" : cp3);
256     if (cp3 != NULL)
257       free(cp3);
258     if (cp2 != NULL)
259       free(cp2);
260     / *05 cp1 was given away * /
261   }
262   catch(ex) {
263     / *05 global context untouched * /
264     rethrow;
265   }
266 }\endverbatim
267
268  *
269  * @{
270  */
271
272 /** @brief Structure describing an exception */
273 typedef struct {
274   char *msg;      /**< human readable message; to be freed */
275   int   category; /**< category like HTTP (what went wrong) */
276   int   value;    /**< like errno (why did it went wrong) */
277   /* throw point */
278   char *host;     /* NULL for localhost; hostname:port if remote */
279   char *procname; 
280   char *file;     /**< to be freed only for remote exceptions */
281   int   line;     
282   char *func;     /**< to be freed only for remote exceptions */
283 } ex_t;
284
285 /* declare the context type (private) */
286 typedef struct {
287     __ex_mctx_t  *ctx_mctx;     /* permanent machine context of enclosing try/catch */
288     int           ctx_deferred; /* permanent flag whether exception is deferred */
289     int           ctx_deferring;/* permanent counter of exception deferring level */
290     int           ctx_defer;    /* temporary flag for exception deferring macro */
291     int           ctx_shielding;/* permanent counter of exception shielding level */
292     int           ctx_shield;   /* temporary flag for exception shielding macro */
293     int           ctx_caught;   /* temporary flag whether exception was caught */
294     volatile ex_t ctx_ex;       /* temporary exception storage */
295 } ex_ctx_t;
296
297 /* the static and dynamic initializers for a context structure */
298 #define XBT_CTX_INITIALIZER \
299     { NULL, 0, 0, 0, 0, 0, 0, { /* content */ NULL, 0, 0, \
300                                 /*throw point*/ NULL, NULL, NULL, 0, NULL } }
301 #define XBT_CTX_INITIALIZE(ctx) \
302     do { \
303         (ctx)->ctx_mctx        = NULL; \
304         (ctx)->ctx_deferred    = 0;    \
305         (ctx)->ctx_deferring   = 0;    \
306         (ctx)->ctx_defer       = 0;    \
307         (ctx)->ctx_shielding   = 0;    \
308         (ctx)->ctx_shield      = 0;    \
309         (ctx)->ctx_caught      = 0;    \
310         (ctx)->ctx_ex.msg      = NULL; \
311         (ctx)->ctx_ex.category = 0;    \
312         (ctx)->ctx_ex.value    = 0;    \
313         (ctx)->ctx_ex.host     = NULL; \
314         (ctx)->ctx_ex.procname = NULL; \
315         (ctx)->ctx_ex.file     = NULL; \
316         (ctx)->ctx_ex.line     = 0;    \
317         (ctx)->ctx_ex.func     = NULL; \
318     } while (0)
319
320 /* the exception context */
321 typedef ex_ctx_t *(*ex_ctx_cb_t)(void);
322 extern ex_ctx_cb_t __xbt_ex_ctx;
323 extern ex_ctx_t *__xbt_ex_ctx_default(void);
324
325 /* the termination handler */
326 typedef void (*ex_term_cb_t)(ex_t *);
327 extern ex_term_cb_t __xbt_ex_terminate;
328 extern void __xbt_ex_terminate_default(ex_t *e);
329
330 /** @brief Introduce a block where exception may be dealed with 
331  *  @hideinitializer
332  */
333 #define xbt_try \
334     { \
335         ex_ctx_t *__xbt_ex_ctx_ptr = __xbt_ex_ctx(); \
336         int __ex_cleanup = 0; \
337         __ex_mctx_t *__ex_mctx_en; \
338         __ex_mctx_t __ex_mctx_me; \
339         __ex_mctx_en = __xbt_ex_ctx_ptr->ctx_mctx; \
340         __xbt_ex_ctx_ptr->ctx_mctx = &__ex_mctx_me; \
341         if (__ex_mctx_save(&__ex_mctx_me)) { \
342             if (1)
343
344 /** @brief optional(!) block for cleanup 
345  *  @hideinitializer
346  */
347 #define xbt_cleanup \
348             else { \
349             } \
350             __xbt_ex_ctx_ptr->ctx_caught = 0; \
351         } \
352         else { \
353             __ex_mctx_restored(&__ex_mctx_me); \
354             __xbt_ex_ctx_ptr->ctx_caught = 1; \
355         } \
356         __xbt_ex_ctx_ptr->ctx_mctx = __ex_mctx_en; \
357         __ex_cleanup = 1; \
358         if (1) { \
359             if (1)
360
361 /** @brief the block for catching (ie, deal with) an exception 
362  *  @hideinitializer
363  */
364 #define xbt_catch(e) \
365             else { \
366             } \
367             if (!(__ex_cleanup)) \
368                 __xbt_ex_ctx_ptr->ctx_caught = 0; \
369         } \
370         else { \
371             if (!(__ex_cleanup)) { \
372                 __ex_mctx_restored(&__ex_mctx_me); \
373                 __xbt_ex_ctx_ptr->ctx_caught = 1; \
374             } \
375         } \
376         __xbt_ex_ctx_ptr->ctx_mctx = __ex_mctx_en; \
377     } \
378     if (   !(__xbt_ex_ctx()->ctx_caught) \
379         || ((e) = __xbt_ex_ctx()->ctx_ex, 0)) { \
380     } \
381     else
382
383 /** @brief Build an exception from the supplied arguments and throws it
384  *  @hideinitializer
385  *
386  * If called from within a sg_try/sg_catch construct, this exception 
387  * is copied into the sg_catch relevant variable program control flow 
388  * is derouted to the sg_catch (after the optional sg_cleanup). 
389  *
390  * If no sg_try/sg_catch conctruct embeeds this call, the program calls
391  * abort(3). 
392  *
393  * The sg_throw can be performed everywhere, including inside sg_try, 
394  * sg_cleanup and sg_catch blocks.
395  */
396 #define xbt_throw(c,v,m) \
397     ((   __xbt_ex_ctx()->ctx_shielding > 0 \
398       || (__xbt_ex_ctx()->ctx_deferring > 0 && __xbt_ex_ctx()->ctx_deferred == 1)) ? 0 : \
399      (__xbt_ex_ctx()->ctx_ex.msg      = bprintf(m), \
400       __xbt_ex_ctx()->ctx_ex.category = (c), \
401       __xbt_ex_ctx()->ctx_ex.value    = (v), \
402       __xbt_ex_ctx()->ctx_ex.host     = (char*)NULL, \
403       __xbt_ex_ctx()->ctx_ex.procname = strdup(xbt_procname()), \
404       __xbt_ex_ctx()->ctx_ex.file     = (char*)__FILE__, \
405       __xbt_ex_ctx()->ctx_ex.line     = __LINE__, \
406       __xbt_ex_ctx()->ctx_ex.func     = (char*)_XBT_FUNCTION, \
407       __xbt_ex_ctx()->ctx_deferred     = 1, \
408       (__xbt_ex_ctx()->ctx_deferring > 0 ? 0 : \
409        (__xbt_ex_ctx()->ctx_mctx == NULL \
410         ? (__xbt_ex_terminate((ex_t *)&(__xbt_ex_ctx()->ctx_ex)), -1) \
411         : (__ex_mctx_restore(__xbt_ex_ctx()->ctx_mctx), 1) ))))
412
413 /** @brief re-throwing of an already caught exception (ie, pass it to the upper catch block) 
414  *  @hideinitializer
415  */
416 #define xbt_rethrow \
417     ((   __xbt_ex_ctx()->ctx_shielding > 0 \
418       || __xbt_ex_ctx()->ctx_deferring > 0) ? 0 : \
419       (  __xbt_ex_ctx()->ctx_mctx == NULL \
420        ? (__xbt_ex_terminate((ex_t *)&(__xbt_ex_ctx()->ctx_ex)), -1) \
421        : (__ex_mctx_restore(__xbt_ex_ctx()->ctx_mctx), 1) ))
422
423 /** @brief shield an operation from exception handling 
424  *  @hideinitializer
425  */
426 #define xbt_shield \
427     for (__xbt_ex_ctx()->ctx_shielding++, \
428          __xbt_ex_ctx()->ctx_shield =  1; \
429          __xbt_ex_ctx()->ctx_shield == 1; \
430          __xbt_ex_ctx()->ctx_shield =  0, \
431          __xbt_ex_ctx()->ctx_shielding--)
432
433 /** @brief defer immediate exception handling 
434  *  @hideinitializer
435  */
436 #define xbt_defer \
437     for (((__xbt_ex_ctx()->ctx_deferring)++ == 0 ? __xbt_ex_ctx()->ctx_deferred = 0 : 0), \
438          __xbt_ex_ctx()->ctx_defer =  1;  \
439          __xbt_ex_ctx()->ctx_defer == 1;  \
440          __xbt_ex_ctx()->ctx_defer =  0,  \
441          ((--(__xbt_ex_ctx()->ctx_deferring) == 0 && __xbt_ex_ctx()->ctx_deferred == 1) ? xbt_rethrow : 0))
442
443 /** @brief exception handling tests 
444  *  @hideinitializer
445  */
446 #define xbt_catching \
447     (__xbt_ex_ctx()->ctx_mctx != NULL)
448 /** @brief exception handling tests 
449  *  @hideinitializer
450  */
451 #define xbt_shielding \
452     (__xbt_ex_ctx()->ctx_shielding > 0)
453 /** @brief exception handling tests 
454  *  @hideinitializer
455  */
456 #define xbt_deferring \
457     (__xbt_ex_ctx()->ctx_deferring > 0)
458
459 /* optional namespace mapping */
460 #if defined(__EX_NS_UCCXX__)
461 #define Try      xbt_try
462 #define Cleanup  xbt_cleanup
463 #define Catch    xbt_catch
464 #define Throw    xbt_throw
465 #define Rethrow  xbt_rethrow
466 #define Shield   xbt_shield
467 #define Defer    xbt_defer
468 #elif defined(__EX_NS_CXX__) || (!defined(__cplusplus) && !defined(__EX_NS_CUSTOM__))
469 #define try      xbt_try
470 #define cleanup  xbt_cleanup
471 #define catch    xbt_catch
472 #define throw    xbt_throw
473 #define rethrow  xbt_rethrow
474 #define shield   xbt_shield
475 #define defer    xbt_defer
476 #endif
477
478 /** @} */
479 #endif /* __XBT_EX_H__ */
480