head 1.34; access; symbols EX_1_0_6:1.34 LMTP2NNTP_1_4_1:1.31 LMTP2NNTP_1_4_0:1.31 EX_1_0_5:1.31 LMTP2NNTP_1_3_0:1.29 LMTP2NNTP_1_3b2:1.29 LMTP2NNTP_1_3b1:1.29 LMTP2NNTP_1_3a3:1.29 LMTP2NNTP_1_3a2:1.29 LMTP2NNTP_1_3a1:1.29 EX_1_0_4:1.29 EX_1_0_3:1.29 OSSP_RC_0_7_3:1.28 OSSP_RC_0_7_2:1.28 OSSP_RC_0_7_1:1.28 OSSP_RC_ALPHA_06:1.28 OSSP_RC_EXBROKEN:1.28 LMTP2NNTP_1_2_0:1.28 LMTP2NNTP_1_2b4:1.28 LMTP2NNTP_1_2b3:1.28 LMTP2NNTP_1_2b2:1.28 LMTP2NNTP_1_2b1:1.28 LMTP2NNTP_1_2a8:1.28 LMTP2NNTP_1_2a7:1.28 EX_1_0_2:1.28 EX_1_0_1:1.26 LMTP2NNTP_1_2a6:1.25 LMTP2NNTP_1_2a5:1.25 EX_1_0_0:1.23 LMTP2NNTP_1_2a4:1.21 EX_0_9_1:1.21 LMTP2NNTP_1_2a3:1.20 EX_0_9_0:1.19; locks; strict; comment @# @; 1.34 date 2007.10.12.19.59.57; author rse; state Exp; branches; next 1.33; commitid 9SN7ivPOxnsC7lBs; 1.33 date 2007.10.12.19.57.56; author rse; state Exp; branches; next 1.32; commitid fT5INtDxwWhR6lBs; 1.32 date 2006.08.10.19.49.33; author rse; state Exp; branches; next 1.31; commitid o4A6XFppdzD0PkIr; 1.31 date 2005.01.30.13.58.18; author rse; state Exp; branches; next 1.30; 1.30 date 2005.01.30.13.39.07; author rse; state Exp; branches; next 1.29; 1.29 date 2004.03.25.19.01.42; author rse; state Exp; branches; next 1.28; 1.28 date 2003.01.30.11.04.42; author rse; state Exp; branches; next 1.27; 1.27 date 2003.01.30.10.23.12; author rse; state Exp; branches; next 1.26; 1.26 date 2003.01.06.15.31.24; author rse; state Exp; branches; next 1.25; 1.25 date 2002.04.02.17.54.09; author rse; state Exp; branches; next 1.24; 1.24 date 2002.04.02.06.36.38; author thl; state Exp; branches; next 1.23; 1.23 date 2002.03.30.18.55.28; author rse; state Exp; branches; next 1.22; 1.22 date 2002.03.30.18.15.49; author rse; state Exp; branches; next 1.21; 1.21 date 2002.03.07.21.30.10; author rse; state Exp; branches; next 1.20; 1.20 date 2002.02.25.10.30.15; author rse; state Exp; branches; next 1.19; 1.19 date 2002.01.31.20.48.05; author rse; state Exp; branches; next 1.18; 1.18 date 2002.01.31.20.23.06; author rse; state Exp; branches; next 1.17; 1.17 date 2002.01.31.20.22.03; author rse; state Exp; branches; next 1.16; 1.16 date 2002.01.30.13.39.41; author rse; state Exp; branches; next 1.15; 1.15 date 2002.01.30.12.34.41; author rse; state Exp; branches; next 1.14; 1.14 date 2002.01.30.12.28.37; author rse; state Exp; branches; next 1.13; 1.13 date 2002.01.30.12.17.58; author rse; state Exp; branches; next 1.12; 1.12 date 2002.01.30.11.55.52; author rse; state Exp; branches; next 1.11; 1.11 date 2002.01.30.11.11.09; author rse; state Exp; branches; next 1.10; 1.10 date 2002.01.30.10.33.36; author rse; state Exp; branches; next 1.9; 1.9 date 2002.01.29.20.10.53; author rse; state Exp; branches; next 1.8; 1.8 date 2002.01.29.11.05.21; author rse; state Exp; branches; next 1.7; 1.7 date 2002.01.26.22.43.54; author rse; state Exp; branches; next 1.6; 1.6 date 2002.01.26.22.42.46; author rse; state Exp; branches; next 1.5; 1.5 date 2002.01.26.22.35.02; author rse; state Exp; branches; next 1.4; 1.4 date 2002.01.25.22.23.55; author rse; state Exp; branches; next 1.3; 1.3 date 2002.01.25.22.23.17; author rse; state Exp; branches; next 1.2; 1.2 date 2002.01.25.15.30.05; author rse; state Exp; branches; next 1.1; 1.1 date 2002.01.25.15.25.51; author rse; state Exp; branches; next ; desc @@ 1.34 log @prepare for release @ text @## ## OSSP ex - Exception Handling ## Copyright (c) 2002-2007 Ralf S. Engelschall ## Copyright (c) 2002-2007 The OSSP Project ## ## This file is part of OSSP ex, an exception library ## which can be found at http://www.ossp.org/pkg/lib/ex/. ## ## Permission to use, copy, modify, and distribute this software for ## any purpose with or without fee is hereby granted, provided that ## the above copyright notice and this permission notice appear in all ## copies. ## ## THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED ## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ## IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR ## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF ## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, ## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT ## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ## SUCH DAMAGE. ## ## ex.pod: exception library manual page ## =pod =head1 NAME B - Exception Handling =head1 VERSION B =head1 SYNOPSIS B I; B I [B I] B (I) I B(I, I, I); B; B I B I if (B) ... if (B) ... if (B) ... =head1 DESCRIPTION B is a small B style exception handling library for use in the B language. It allows you to use the paradigm of throwing and catching exceptions in order to reduce the amount of error handling code without making your program less robust. 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. =head2 EXCEPTIONS An B exception is a triple EI,I,IE where I identifies the class of the exception thrower, I identifies the particular class instance of the exception thrower, and I is the exceptional return code value the thrower wants to communicate. All three parts are of type "C" internally, but every value which can be lossless "casted" to this type is usable. Exceptions are created on-the-fly by the B command. =head2 APPLICATION PROGRAMMER INTERFACE (API) The B API consists of the following elements: =over 4 =item B I; This is the declaration of an exception variable. It is usually never initialized manually. Instead it is initialized by an B clause and just used read-only inside its block. Such a variable of type B consists of six attributes: =over 2 =item CI This is the I argument of the B call which created the exception. This can globally and uniquely identify the class to which I belongs to. Usually this is a pointer to a static object (variable, structure or function) which identifies the class of the thrower and allows the catcher to correctly handle I. It is usually just an additional (optional) information to I. =item CI This is the I argument of the B call which created the exception. This can globally and uniquely identify the class instance I belongs to (in case multiple instances exists at all). Usually this a pointer to a dynamic object (structure) which identifiers the particular instance of the thrower. It is usually just an additional (optional) information to I. =item CI This is the I argument of the B call which created the exception. This is the exceptional return code value which has to uniquely identify the type of exception. Usually this is the value which is Ced if no exceptions would be thrown. In the simple case this is just a numerical return code. In the complex case this can be a pointer to an arbitrary complex data structure describing the exception. =item CI This is the file name of the B source where the B call was performed. It is automatically provided as an additional information about the throw point and is intended mainly for tracing and debugging purposes. =item C I This is the line number inside the B source file name where the B call was performed. It is automatically provided as an additional information about the throw point and is intended mainly for tracing and debugging purposes. =item CI This is the function name (if determinable, else "C<#NA#>") inside the B source file name where the B call was performed. It is automatically provided as an additional information about the throw point and is intended mainly for tracing and debugging purposes. =back =item B I [B I] B (I) I This is the primary syntactical construct provided by B. It is modeled after the B C-C clause which in turn is very similar to an B C-C clause. It consists of an B block I which forms the dynamic scope for exception handling (i.e. exceptions directly thrown there or thrown from its sub-routines are caught), an optional B block I for performing cleanup operations and an B block I where the caught exceptions are handled. The control flow in case no exception is thrown is simply I, optionally followed by I; I is skipped. The control flow in case an exception is thrown is: I (up to the statement where the exception is thrown only), optionally followed by I, followed by I. The B, B and B cannot be used separately, they work only in combination because they form a language clause as a whole. In contrast to B there is only one B block and not multiple ones (all B exceptions are of the same B type B). If an exception is caught, it is stored in I for inspection inside the B block. Although having to be declared outside, the I value is only valid within the B block. But the variable can be re-used in subsequent B clauses, of course. The B block is a regular B language statement block, but it is not allowed to jump into it via "C" or C(3) or out of it via "C", "C", "C" or C(3) because there is some hidden setup and cleanup that needs to be done by B regardless of whether an exception is caught. Jumping into an B clause would avoid doing the setup, and jumping out of it would avoid doing the cleanup. In both cases the result is a broken exception handling facility. Nevertheless you are allowed to nest B clauses. The B and B blocks are regular B language statement blocks without any restrictions. You are even allowed to throw (and in the B block to re-throw) an exception. There is just one subtle portability detail you have to remember about B blocks: all accessible B objects have the (expected) values as of the time B was called, except that the values of objects of automatic storage invocation duration that do not have the "C" storage class I have been changed between the B invocation and B are indeterminate. This is because both you usually do not know which commands in the B were already successful before the exception was thrown (logically speaking) and because the underlying B setjmp(3) facility applies those restrictions (technically speaking). =item B(I, I, I); This builds an exception from the supplied arguments and throws it. If an B/B clause formed the dynamic scope of the B call, this exception is copied into the I of its B clause and the program control flow is continued in the (optional B and then in the) B block. If no B/B clause exists in the dynamic scope of the B call, the program calls C(3). The B can be performed everywhere, including inside B, B and B blocks. =item B; This is only valid within an B block and re-throws the current exception (in I). It is similar to the call B(I.ex_class, I.ex_object, I.ex_value) except for the difference that the C, C and C elements of the caught exception are passed through as it would have been never caught. =item B I This directive executes I while deferring the throwing of exceptions, i.e., inside the dynamic scope of B all B operations are remembered but deferred and on leaving the I the I occurred exception is thrown. The second and subsequent exceptions are ignored. The B block I is a regular B language statement block, but it is not allowed to jump into it via "C" or C(3) or out of it via "C", "C", "C" or C(3) because this would cause the deferral level to become out of sync. Jumping into an B clause would avoid increasing the exception deferral level, and jumping out of it would avoid decreasing it. In both cases the result is an incorrect exception deferral level. Nevertheless you are allowed to nest B clauses. =item B I This directive executes I while shielding it against the throwing of exceptions, i.e., inside the dynamic scope of B all B operations are just silently ignored. The B block is a regular B language statement block, but it is not allowed to jump into it via "C" or C(3) or out of it via "C", "C", "C" or C(3) because this would cause the shielding level to become out of sync. Jumping into an B clause would avoid increasing the exception shielding level, and jumping out of it would avoid decreasing it. In both cases the result is an incorrect exception shielding level. Nevertheless you are allowed to nest B clauses. =item B This is a boolean flag which can be checked inside the dynamic scope of an B clause to test whether the current scope is exception catching (see B/B clause). =item B This is a boolean flag which can be checked inside the dynamic scope of an B clause to test whether the current scope is exception deferring (see B clause). =item B This is a boolean flag which can be checked inside the dynamic scope of an B clause to test whether the current scope is exception shielding (see B clause). =back =head1 IMPLEMENTATION CONTROL B uses a very light-weight but still flexible exception facility implementation. The following adjustments can be made before including the F header: =head2 Machine Context In order to move the program control flow from the exception throw point (B) to the catch point (B), B uses four macros: =over 4 =item B<__ex_mctx_struct> This holds the contents of the machine context structure. A pointer to such a machine context is passed to the following macros as I. =item B<__ex_mctx_save>(__ex_mctx_struct *I) This is called by the prolog of B to save the current machine context in I. This function has to return true (not C<0>) after saving. If the machine context is restored (by B<__ex_mctx_restore>) it has to return false (C<0>). In other words, this function has to return twice and indicate the particular situation with the provided return code. =item B<__ex_mctx_restored>(__ex_mctx_struct *I) This is called by the epilog of B to perform additional operations at the new (restored) machine context after an exception was caught. Usually this is a no-operation macro. =item B<__ex_mctx_restore>(__ex_mctx_struct *I) This is called by B at the old machine context in order to restore the machine context of the B/B clause which will catch the exception. =back The default implementation (define C<__EX_MCTX_SJLJ__> or as long as C<__EX_MCTX_CUSTOM__> is not defined) uses the B jmp_buf(3) facility: #define __ex_mctx_struct jmp_buf jb; #define __ex_mctx_save(mctx) (setjmp((mctx)->jb) == 0) #define __ex_mctx_restored(mctx) /* noop */ #define __ex_mctx_restore(mctx) (void)longjmp((mctx)->jb, 1) Alternatively, you can define C<__EX_MCTX_SSJLJ__> to use B sigjmp_buf(3) or C<__EX_MCTX_MCSC__> to use B ucontext(3). For using a custom implementation define C<__EX_MCTX_CUSTOM__> and provide own definitions for the four B<__ex_mctx_xxxx> macros. =head2 Exception Context In order to maintain the exception catching stack and for passing the exception between the throw and the catch point, B uses a global exception context, returned on-the-fly by the callback "CB<__ex_ctx>C<)(void)>". By default, B<__ex_ctx> (which is B<__ex_ctx_default> as provided by F) returns a pointer to a static C context. For use in multi-threading environments, this should be overwritten with a callback function returning a per-thread context structure (see section B below). To initialize an exception context structure there are two macros defined: "B" for static initialization and "CBC<(ex_ctx_t *)>" for dynamic initialization. =head2 Termination Handler In case there is an exception thrown which is not caught by any B/B clauses, B calls the callback "CB<__ex_terminate>C<)(ex_t *)>". It receives a pointer to the exception object which was thrown. By default, B<__ex_terminate> (which is B<__ex_terminate_default> as provided by F) prints a message of the form "C<**EX: UNCAUGHT EXCEPTION: class=0xXXXXXXXX object=0xXXXXXXXX value=0xXXXXXXX [xxxx:NNN:xxxx]>" to F and then calls abort(3) in order to terminate the application. For use in multi-threading environments, this should be overwritten with a callback function which terminates only the current thread. Even better, a real application always should have a top-level B/B clause in its "C" in order to more gracefully terminate the application. =head2 Namespace Mapping The B implementation consistently uses the "C", "C<__ex_>" and "C<__EX_>" prefixes for namespace protection. But at least the "C" prefix for the API macros B, B, B, B, B and B sometimes have an unpleasant optical appearance. Especially because B is modeled after the exception facility of B where there is no such prefix on the language directives, of course. For this, B optionally provides the ability to provide additional namespace mappings for those API elements. By default (define C<__EX_NS_CXX__> or as long as C<__EX_NS_CUSTOM__> and C<__cplusplus> is not defined) you can additionally use the B style names B, B, B, B and B. As an alternative you can define C<__EX_NS_UCCXX__> to get the same but with a more namespace safe upper case first letter. =head1 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): /* BAD EXAMPLE */ ex_try { char *cp1, *cp2, cp3; cp1 = mallocex(SMALLAMOUNT); globalcontext->first = cp1; cp2 = mallocex(TOOBIG); cp3 = mallocex(SMALLAMOUNT); strcpy(cp1, "foo"); strcpy(cp2, "bar"); } ex_cleanup { if (cp3 != NULL) free(cp3); if (cp2 != NULL) free(cp2); if (cp1 != NULL) free(cp1); } ex_catch(ex) { printf("cp3=%s", cp3); ex_rethrow; } This example raises a few issues: =over 4 =item B<01: variable scope> Variables which are used in the B or B clauses must be declared before the B clause, otherwise they only exist inside the B block. In the example above, C, C and C are automatic variables and only exist in the block of the B clause, the code in the B and B clauses does not know anything about them. =item B<02: variable initialization> Variables which are used in the B or B clauses must be initialized before the point of the first possible B is reached. In the example above, B would have trouble using C if mallocex() throws a exception when allocating a C buffer. =item B<03: volatile variables> Variables which are used in the B or B clauses must be declared with the storage class "C", otherwise they might contain outdated information if B throws an exception. If using a "free if unset" approach like the example does in the B clause, the variables must be initialized (see B<02>) I remain valid upon use. =item B<04: clean before catch> The B clause is not only written down before the B clause, it is also evaluated before the B clause. So, resources being cleaned up must no longer be used in the B block. The example above would have trouble referencing the character strings in the printf(3) statement because these have been freed before. =item B<05: variable uninitialization> If resources are passed away and out of the scope of the B/B/B construct and the variables were initialized for using a "free if unset" approach then they must be uninitialized after being passed away. The example above would free(3) C in the B clause if mallocex() throws an exception if allocating a C buffer. The C pointer hence becomes invalid. =back The following is fixed version of the code (annotated with the pitfall items for reference): /* GOOD EXAMPLE */ { /*01*/ char * volatile /*03*/ cp1 = NULL /*02*/; char * volatile /*03*/ cp2 = NULL /*02*/; char * volatile /*03*/ cp3 = NULL /*02*/; try { cp1 = mallocex(SMALLAMOUNT); globalcontext->first = cp1; cp1 = NULL /*05 give away*/; cp2 = mallocex(TOOBIG); cp3 = mallocex(SMALLAMOUNT); strcpy(cp1, "foo"); strcpy(cp2, "bar"); } clean { /*04*/ printf("cp3=%s", cp3 == NULL /*02*/ ? "" : cp3); if (cp3 != NULL) free(cp3); if (cp2 != NULL) free(cp2); /*05 cp1 was given away */ } catch(ex) { /*05 global context untouched */ rethrow; } } Alternatively, this could also be used: /* ALTERNATIVE GOOD EXAMPLE */ { /*01*/ char * volatile /*03*/ cp1 = NULL /*02*/; char * volatile /*03*/ cp2 = NULL /*02*/; char * volatile /*03*/ cp3 = NULL /*02*/; try { cp1 = mallocex(SMALLAMOUNT); globalcontext->first = cp1; /*05 keep responsibility*/ cp2 = mallocex(TOOBIG); cp3 = mallocex(SMALLAMOUNT); strcpy(cp1, "foo"); strcpy(cp2, "bar"); } clean { /*04*/ printf("cp3=%s", cp3 == NULL /*02*/ ? "" : cp3); if (cp3 != NULL) free(cp3); if (cp2 != NULL) free(cp2); if (cp1 != NULL) free(cp1); } catch(ex) { globalcontext->first = NULL; rethrow; } } =head1 MULTITHREADING ENVIRONMENTS B is designed to work both in single-threading and multi-threading environments. The default is to support single-threading only. But it is easy to configure B to work correctly in a multi-threading environment like B or B. There are only two issues: which machine context to use and where to store the exception context to make sure exception throwing happens only within a thread and does not conflict with the regular thread dispatching mechanism. =head2 GNU pth Using B together with B is straight-forward, because B 2.0 (and higher) already has support for B built-in. All which is needed is that B is configured with the B option C<--with-ex>. Then each B user-space thread has its own B exception context automatically. The default of using B jmp_buf(3) does not conflict with the thread dispatching mechanisms used by B. =head2 POSIX pthreads Using B inside an arbitrary B standard compliant environment is also straight-forward, although it requires extra coding. What you basically have to do is to make sure that the B<__ex_ctx> becomes a per-thread context and that B<__ex_terminate> terminates only the current thread. To get an impression, a small utility library for this follows: =over 2 =item F #ifndef __PTHREAD_EX_H__ #define __PTHREAD_EX_H__ #include int pthread_init_ex (void); int pthread_create_ex (pthread_t *, const pthread_attr_t *, void *(*)(void *), void *); #ifndef PTHREAD_EX_INTERNAL #define pthread_init pthread_init_ex #define pthread_create pthread_create_ex #endif #endif /* __PTHREAD_EX_H__ */ =item F #include #include #define PTHREAD_EX_INTERNAL #include "pthread_ex.h" #include "ex.h" /* context storage key */ static pthread_key_t pthread_ex_ctx_key; /* context destructor */ static void pthread_ex_ctx_destroy(void *data) { if (data != NULL) free(data); return; } /* callback: context fetching */ static ex_ctx_t *pthread_ex_ctx(void) { ex_ctx_t *ctx; if ((ctx = (ex_ctx_t *)pthread_getspecific(pthread_ex_ctx_key)) == NULL) return __ex_ctx_default(); return ctx; } /* callback: termination */ static void pthread_ex_terminate(ex_t *e) { ex_ctx_t *ctx; if ((ctx = (ex_ctx_t *)pthread_getspecific(pthread_ex_ctx_key)) == NULL) __ex_terminate_default(e); pthread_exit(e->ex_value); } /* pthread init */ int pthread_init_ex(void) { int rc; /* additionally create thread data key and override OSSP ex callbacks */ pthread_key_create(&pthread_ex_ctx_key, pthread_ex_ctx_destroy); __ex_ctx = pthread_ex_ctx; __ex_terminate = pthread_ex_terminate; return rc; } /* internal thread entry wrapper information */ typedef struct { void *(*entry)(void *); void *arg; } pthread_create_ex_t; /* internal thread entry wrapper */ static void *pthread_create_wrapper(void *arg) { pthread_create_ex_t *wrapper; ex_ctx_t *ex_ctx; /* create per-thread exception context */ wrapper = (pthread_create_ex_t *)arg; ex_ctx = (ex_ctx_t *)malloc(sizeof(ex_ctx_t)); EX_CTX_INITIALIZE(ex_ctx); pthread_setspecific(pthread_ex_ctx_key, ex_ctx); /* perform original operation */ return wrapper->entry(wrapper->arg); } /* pthread_create() wrapper */ int pthread_create_ex(pthread_t *thread, const pthread_attr_t *attr, void *(*entry)(void *), void *arg) { pthread_create_ex_t wrapper; /* spawn thread but execute start function through wrapper */ wrapper.entry = entry; wrapper.arg = arg; return pthread_create(thread, attr, pthread_create_wrapper, &wrapper); } =back Now all which is required is that you include F after the standard F header and to call B once at startup of your program. =head1 EXAMPLES As a real-life example we will look how you can add optional B based exception handling support to a library B. The original library looks like this: =over 2 =item F typedef enum { FOO_OK, FOO_ERR_ARG, FOO_ERR_XXX, FOO_ERR_SYS, FOO_ERR_IMP, ... } foo_rc_t; struct foo_st; typedef struct foo_st foo_t; foo_rc_t foo_create (foo_t **foo); foo_rc_t foo_perform (foo_t *foo); foo_rc_t foo_destroy (foo_t *foo); =item F #include "foo.h" struct foo_st { ... } foo_rc_t foo_create(foo_t **foo) { if ((*foo = (foo_t)malloc(sizeof(foo))) == NULL) return FOO_ERR_SYS; (*foo)->... = ... return FOO_OK; } foo_rc_t foo_perform(foo_t *foo) { if (foo == NULL) return FOO_ERR_ARG; if (...) return FOO_ERR_XXX; return FOO_OK; } foo_rc_t foo_destroy(foo_t *foo) { if (foo == NULL) return FOO_ERR_ARG; free(foo); return FOO_OK; } =back Then the typical usage of this library is: #include "foo.h" ... foo_t foo; foo_rc_t rc; ... if ((rc = foo_create(&foo)) != FOO_OK) die(rc); if ((rc = foo_perform(foo)) != FOO_OK) die(rc); if ((rc = foo_destroy(foo)) != FOO_OK) die(rc); But what you really want, is to use exception handling to get rid of the intermixed error handling code: #include "foo.h" #include "ex.h" ... foo_t foo; ex_t ex; ... ex_try { foo_create(&foo); foo_perform(foo); foo_destroy(foo); } ex_catch (ex) { die((foo_rc_t)ex->ex_value); } You can achieve this very easily by changing the library as following: =over 2 =item F ... extern const char foo_id[]; ... =item F #include "foo.h" const char foo_id[] = "foo 1.0"; #ifdef WITH_EX #include "ex.h" #define FOO_RC(rv) \ ( (rv) != FOO_OK && (ex_catching && !ex_shielding) \ ? (ex_throw(foo_id, NULL, (rv)), (rv)) : (rv) ) #else #define FOO_RC(rv) (rv) #endif struct foo_st { ... } foo_rc_t foo_create(foo_t **foo) { if ((*foo = (foo_t)malloc(sizeof(foo))) == NULL) return FOO_RC(FOO_ERR_SYS); (*foo)->... = ... return FOO_OK; } foo_rc_t foo_perform(foo_t *foo) { if (foo == NULL) return FOO_RC(FOO_ERR_ARG); if (...) return FOO_RC(FOO_ERR_XXX); return FOO_OK; } foo_rc_t foo_destroy(foo_t *foo) { if (foo == NULL) return FOO_RC(FOO_ERR_ARG); free(foo); return FOO_OK; } =back This way the library by default is still exactly the same. If you now compile it with C<-DWITH_EX> you activate exception handling support. This means that all API functions throw exceptions where C is the C instead of returning this value. =head1 SEE ALSO B C, C, C. B C, C, C, C. B jmp_buf(3), setjmp(3), longjmp(3). B sigjmp_buf(3), sigsetjmp(3), siglongjump(3). B ucontext(3), setcontext(3), getcontext(3). =head1 HISTORY B was invented in January 2002 by Ralf S. Engelschall Erse@@engelschall.comE for use inside the B project. Its creation was prompted by the requirement to reduce the error handling inside B. The core B/B clause was inspired by B and the implementation was partly derived from B 2.0.0, a similar library written 2000 by Adam M. Costello Eamc@@cs.berkeley.eduE and Cosmin Truta Ecosmin@@cs.toronto.eduE. The B clause was inspired by the B C clause. The B feature was inspired by an "C" shielding facility used in the B implementation. The B feature was invented to simplify an application's cleanup handling if multiple independent resources are allocated and have to be freed on error. =head1 AUTHORS Ralf S. Engelschall rse@@engelschall.com www.engelschall.com =cut @ 1.33 log @Fix example given under "MULTITHREADING ENVIRONMENTS" in the manual page. The problem is that the context can be still not associated and hence be NULL. Submitted by: Frank Hempel @ text @d3 2 a4 2 ## Copyright (c) 2002-2006 Ralf S. Engelschall ## Copyright (c) 2002-2006 The OSSP Project @ 1.32 log @cleanup source tree @ text @d601 5 a605 2 return (ex_ctx_t *) pthread_getspecific(pthread_ex_ctx_key); d611 4 @ 1.31 log @some cleanups to the manual page @ text @d3 2 a4 3 ## Copyright (c) 2002-2005 Ralf S. Engelschall ## Copyright (c) 2002-2005 The OSSP Project ## Copyright (c) 2002-2005 Cable & Wireless @ 1.30 log @Adjusted copyright messages to include new year 2005. @ text @d82 3 a84 3 of type "C" internally, but every value is useable which can be lossless "casted" to this type. Exceptions are created on-the-fly by the B command. d105 1 a105 1 which I belongs to . Usually this is a pointer to a static @ 1.29 log @bump and adjust copyright @ text @d3 3 a5 3 ## Copyright (c) 2002-2004 Ralf S. Engelschall ## Copyright (c) 2002-2004 The OSSP Project ## Copyright (c) 2002-2004 Cable & Wireless @ 1.28 log @final polishing @ text @d3 3 a5 3 ## Copyright (c) 2002-2003 Ralf S. Engelschall ## Copyright (c) 2002-2003 The OSSP Project ## Copyright (c) 2002-2003 Cable & Wireless Germany @ 1.27 log @Polished and enhanced the manual page ex(3) @ text @d5 1 a5 1 ## Copyright (c) 2002-2003 Cable & Wireless Deutschland @ 1.26 log @- consistently use "Exception Handling" - strip trailing whitespaces - adjust copyright for new year 2003 - consistently use OSSP ASCII-art @ text @d82 2 a83 2 of type "C" internally, but every value is useable which can be lossless casted this type. Exceptions are created on-the-fly by the d104 5 a108 4 the exception. This should globally and uniquely identify the class of I. Usually this is a pointer to a static object (variable, structure or function) which identifies the class of the thrower and allows the catcher to correctly handle I. d112 3 a114 3 This is the I argument of the B call which created the exception. This should globally and uniquely identify the class instance of I (in case multiple instances exists at all). d117 1 a117 1 information to I. d131 3 a133 2 was performed. It is provided as an additional information about the throw point and is intended mainly for tracing and debugging purposes. d137 4 a140 4 This is the line number inside the B source file name where the B call was performed. It is provided as an additional information about the throw point and is intended mainly for tracing and debugging purposes. d146 2 a147 2 is provided as an additional information about the throw point and is intended mainly for tracing and debugging purposes. d179 2 a180 2 it is not allowed to jump into it via C or C(3) or out of it via C, C, C or C(3) because there d196 1 a196 1 the C storage class I have been changed between the d207 6 a212 5 B call, this exception is copied into the I of its B clause and the program control flow is continued in the B block. If no B/B clause exists in the dynamic scope of the B call, the program C(3)s. The B can be performed everywhere, including inside B and d228 12 a239 11 B operations are silently ignored and on leaving the I the first occurred exception is thrown. The B block is a regular B language statement block, but it is not allowed to jump into it via C or C(3) or out of it via C, C, C or C(3) because this would cause the deferral level to become out of sync. Jumping into an B clause would avoid increasing the exception deferral level, and jumping out of it would avoid decreasing it. In both cases the result is an incorrect exception deferral level. Nevertheless you are allowed to nest B clauses. d245 1 a245 1 B operations are silently ignored. d248 7 a254 7 but it is not allowed to jump into it via C or C(3) or out of it via C, C, C or C(3) because this would cause the shielding level to become out of sync. Jumping into an B clause would avoid increasing the exception shielding level, and jumping out of it would avoid decreasing it. In both cases the result is an incorrect exception shielding level. Nevertheless you are allowed to nest B clauses. d260 1 a260 1 catching. d266 1 a266 1 deferring. d272 1 a272 1 shielding. d318 1 a318 1 The default implementation (define C<__EX_MCTX_SJLJ__> or as long d341 3 a343 2 multi-threading environments, this should be overwritten with a callback function returning a per-thread context structure. d355 1 a355 1 exception object which should have been thrown. d362 4 a365 2 should be overwritten with a callback function which terminates only the current thread. d405 1 a405 1 ex_clean { d421 1 a421 1 Variables which are used in the B or B clauses d425 1 a425 1 B clause, the code in the B and B clauses d430 1 a430 1 Variables which are used in the B or B clauses must d432 1 a432 1 reached. In the example above, B would have trouble using d438 2 a439 2 Variables which are used in the B or B clauses must be declared with the storage class C, otherwise they d442 1 a442 1 B clause, the variables must be initialized (see B<02>) I d447 1 a447 1 The B clause is not only written down before the B d451 1 a451 1 strings in the printf(3) statement because these have been free(3)'d d457 1 a457 1 B/B/B construct and the variables were d460 1 a460 1 C in the B clause if mallocex() throws an exception if d466 1 a466 1 As following is fixed version of the code (annotated with the pitfall d471 3 a473 3 volatile /*03*/ char *cp1 = NULL /*02*/; volatile /*03*/ char *cp2 = NULL /*02*/; volatile /*03*/ char *cp3 = NULL /*02*/; d483 2 a484 2 clean { /*04*/ printf("cp3=%s", cp3 == NULL /*02*/ ? "" : cp3); d497 1 a497 1 An alternative fixed version could also be: d499 1 a499 1 /* ALTERNATE *05* GOOD EXAMPLE */ d501 3 a503 3 volatile /*03*/ char *cp1 = NULL /*02*/; volatile /*03*/ char *cp2 = NULL /*02*/; volatile /*03*/ char *cp3 = NULL /*02*/; d513 2 a514 2 clean { /*04*/ printf("cp3=%s", cp3 == NULL /*02*/ ? "" : cp3); a527 1 d543 1 a543 1 B 1.5 (and higher) already has support for B built-in. d546 3 a548 3 has its own B exception context automatically. The default of using B jmp_buf(3) does not conflict with the thread dispatching mechanisms used by B. d559 1 a559 1 =over 4 d569 2 a570 1 int pthread_create_ex (pthread_t *, const pthread_attr_t *, void *(*)(void *), void *); d602 2 a603 1 return (ex_ctx_t *)pthread_getspecific(pthread_ex_ctx_key); d617 4 a620 2 /* additionally create thread data key and override OSSP ex callbacks */ pthread_key_create(&pthread_ex_ctx_key, pthread_ex_ctx_destroy); d650 3 a652 1 int pthread_create_ex(pthread_t *thread, const pthread_attr_t *attr, void *(*entry)(void *), void *arg) d656 2 a657 1 /* spawn thread but execute start function through wrapper */ d660 2 a661 1 return pthread_create(thread, attr, pthread_create_wrapper, &wrapper); d765 1 a765 1 =over 4 d849 1 a849 1 The B feature was inspired by an C shielding facility @ 1.25 log @if we use printf(3), better to also use free(3) for consitency @ text @d2 4 a5 4 ## OSSP ex - Exception Handling Library ## Copyright (c) 2002 Ralf S. Engelschall ## Copyright (c) 2002 The OSSP Project ## Copyright (c) 2002 Cable & Wireless Deutschland d66 1 a66 1 handling code without making your program less robust. d127 1 a127 1 =item CI d165 1 a165 1 d256 1 a256 1 catching. d390 1 a390 1 d478 1 a478 1 if (cp3 != NULL) d480 1 a480 1 if (cp2 != NULL) d482 7 a488 7 /*05 cp1 was given away */ } catch(ex) { /*05 global context untouched */ rethrow; } } d503 1 a503 1 strcpy(cp1, "foo"); d508 1 a508 1 if (cp3 != NULL) d510 1 a510 1 if (cp2 != NULL) d512 1 a512 1 if (cp1 != NULL) d591 1 a591 1 d597 1 a597 1 d603 1 a603 1 d608 1 a608 1 d613 1 a613 1 d616 1 a616 1 d622 1 a622 1 d628 1 a628 1 d634 1 a634 1 d638 1 a638 1 d643 1 a643 1 d764 1 a764 1 @ 1.24 log @typo @ text @d444 1 a444 1 strings in the printf(3) statement because these have been free()d d452 1 a452 1 uninitialized after being passed away. The example above would free() @ 1.23 log @Incorporate Thomas pitfall documentation into ex.pod and this way remove the last item from OSSP ex' TODO list. @ text @d444 1 a444 1 strings in the printf3) statement because these have been free()d @ 1.22 log @fix manual page layouting @ text @d8 1 a8 1 ## which can be found at http://www.ossp.org/pkg/ex/. d377 144 @ 1.21 log @Implement one of the coolest things since sliced bread: deferred exceptions. This allows one to more conviniently program the allocation (and the freeing in case of an error) of multiple (independent) resources. @ text @d413 2 a414 2 #ifndef __PTHREAD_EX_H__ #define __PTHREAD_EX_H__ d416 1 a416 1 #include d418 2 a419 2 int pthread_init_ex (void); int pthread_create_ex (pthread_t *, const pthread_attr_t *, void *(*)(void *), void *); d421 4 a424 4 #ifndef PTHREAD_EX_INTERNAL #define pthread_init pthread_init_ex #define pthread_create pthread_create_ex #endif d426 1 a426 1 #endif /* __PTHREAD_EX_H__ */ d430 9 a438 2 #include #include d440 65 a504 72 #define PTHREAD_EX_INTERNAL #include "pthread_ex.h" #include "ex.h" /* context storage key */ static pthread_key_t pthread_ex_ctx_key; /* context destructor */ static void pthread_ex_ctx_destroy(void *data) { if (data != NULL) free(data); return; } /* callback: context fetching */ static ex_ctx_t *pthread_ex_ctx(void) { return (ex_ctx_t *)pthread_getspecific(pthread_ex_ctx_key); } /* callback: termination */ static void pthread_ex_terminate(ex_t *e) { pthread_exit(e->ex_value); } /* pthread init */ int pthread_init_ex(void) { int rc; /* additionally create thread data key and override OSSP ex callbacks */ pthread_key_create(&pthread_ex_ctx_key, pthread_ex_ctx_destroy); __ex_ctx = pthread_ex_ctx; __ex_terminate = pthread_ex_terminate; return rc; } /* internal thread entry wrapper information */ typedef struct { void *(*entry)(void *); void *arg; } pthread_create_ex_t; /* internal thread entry wrapper */ static void *pthread_create_wrapper(void *arg) { pthread_create_ex_t *wrapper; ex_ctx_t *ex_ctx; /* create per-thread exception context */ wrapper = (pthread_create_ex_t *)arg; ex_ctx = (ex_ctx_t *)malloc(sizeof(ex_ctx_t)); EX_CTX_INITIALIZE(ex_ctx); pthread_setspecific(pthread_ex_ctx_key, ex_ctx); /* perform original operation */ return wrapper->entry(wrapper->arg); } /* pthread_create() wrapper */ int pthread_create_ex(pthread_t *thread, const pthread_attr_t *attr, void *(*entry)(void *), void *arg) { pthread_create_ex_t wrapper; /* spawn thread but execute start function through wrapper */ wrapper.entry = entry; wrapper.arg = arg; return pthread_create(thread, attr, pthread_create_wrapper, &wrapper); } d668 9 a676 5 B C/C/C; B C/C/C/C; B jmp_buf(3)/setjmp(3)/longjmp(3); B sigjmp_buf(3)/sigsetjmp(3)/siglongjump(3); B ucontext(3)/setcontext(3)/getcontext(3). d681 14 a694 12 Erse@@engelschall.comE for use inside the B project. Its creation was prompted by the requirement to reduce the error handling inside B. The core B/B clause was inspired by B and the implementation was partly derived from B 2.0.0, a similar library written 2000 by Adam M. Costello Eamc@@cs.berkeley.eduE and Cosmin Truta Ecosmin@@cs.toronto.eduE. The B clause was inspired by the B C clause. The B feature was inspired by an C shielding facility used in the B implementation. The B feature was invented to simplify an application's cleanup handling if multiple independent resources are allocated and have to be freed on error. @ 1.20 log @Allow ex_shield clauses to be dynamically nested. Without this the whole functionality would be useless in practice. @ text @d51 2 d57 2 d221 16 d258 6 d685 4 a688 1 C shielding facility used in the B implementation. @ 1.19 log @add ex.3 generation @ text @d223 9 @ 1.18 log @fix copyright @ text @d35 5 a39 1 B - Exception Library @ 1.17 log @final polishing @ text @d2 4 a5 2 ## Copyright (c) 2001-2002 The OSSP Project ## Copyright (c) 2001-2002 Cable & Wireless Deutschland @ 1.16 log @mention C++ and Java @ text @d56 8 a63 7 handling code without making your program less robust. 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. d67 7 a73 7 An exception is a triple EI,I,IE where I identifies the class of the exception thrower, I identifies the particular class instance of the exception thrower, and I is the exceptional return code value the thrower wants to send to the catcher. All three parts are of type "C" internally, but every value can be used which can be (automatically) casted to the B type "C". Exceptions are created on-the-fly by the d85 2 a86 2 initialized manually. Instead it is initialized by an B block and just used read-only inside the block. Such a variable of type d94 1 a94 1 the exception. This globally and uniquely identifies the class of d96 2 a97 1 structure or function) which identifies the thrower. d102 5 a106 4 the exception. This globally and uniquely identifies the class instance of I (in case multiple instances exists). Usually this a pointer to a dynamic object (structure) which identifiers the particular instance of the thrower. d110 6 a115 6 This is the I argument of the B call which created the exception. This is the exceptional return code which uniquely identifies the type of exception. Usually this is the value which is Ced if no exceptions would be thrown. In the simple case this is just a numerical return code. In the complex case this can be a pointer to an arbitrary complex data structure describing the exception. d119 3 a121 4 This is the file name of the source where the B call was performed. It is provided as an additional information about the throw point and is intended mainly for tracing and debugging purposes. d125 2 a126 2 This is the line number inside the source file name where the B call was performed. It is provided as an additional d132 3 a134 3 This is the function name (if determinable, else "C<#NA#>") inside the source file name where the B call was performed. It is provided as an additional information about the throw point and is d141 8 a148 8 This is the main construct provided by B. It is modeled after the B C-C clause which in turn is very similar to an B C-C clause. It consists of an B block I which forms the dynamic scope of the exception handling (i.e. exceptions directly thrown there or thrown from its sub-routines are catched), an optional B block I for performing cleanup operations and an B block I where the caught exceptions are handled. d151 4 a154 4 optionally followed by I. I is skipped. The control flow in case an exception is thrown is: I (up to the statement where the exception is thrown), optionally followed by I, followed by I. d156 18 a173 17 The B, B and B cannot be used seperately, they work only in combination. And in contrast to B there is only one B block and not multiple ones (all B exceptions are of the same B type B). If an exception is caught, it is stored in I for inspection (or re-throwing) inside the B block. Although declared outside, the I is only valid within the B block. But it can be re-used in following B/B constructs, of course. The B block is a regular B language block of statement(s), but it is not allowed to jump into it via C or C(3) or out of it via C, C or C(3) because there is some hidden setup and cleanup that needs to be done by B regardless of whether an exception is caught. Jumping into an B clause would avoid doing the setup, and jumping out of it would avoid doing the cleanup. In both cases the result is a broken exception facility. Nevertheless you are allowed to nest B d177 2 a178 2 block of statement(s) without any restrictions. You are even allowed to throw (and in the B block to re-throw) an exception. d182 8 a189 7 values as of the time B was called, except that the values of objects of automatic storage invocation duration that do not have the C storage class I have been changed between the B invocation and B are indeterminate. This is both because you usually do not know which commands in the B were already successful before the exception was thrown and because the underlying B setjmp(3) facility applies those restrictions. d193 6 a198 6 This is second main construct. It builds an exception from the supplied arguments and throws it. If an B/B clause exists in the dynamic scope of the B call, this exception is copied into the I of B and the program control flow is continued in the B block. If no B/B clause exists in the dynamic scope of the B call, the program C(3)s. The d207 3 a209 2 I.ex_value) but with the difference that the C, C and C elements of the thrown exception are kept. d215 1 a215 1 B operations are ignored. d219 3 a221 3 This is a boolean flag which can be tested inside a block to test whether the current scope is exception catching (by B somewhere in the dynamic scope) or not. d225 3 a227 3 This is a boolean flag which can be tested inside a block to test whether the current scope is exception shielding (by B somewhere in the dynamic scope) or not. d247 2 a248 2 This is the contents of the machine context structure. A pointer to such a machine context is passed to the following macros as I. d250 1 a250 1 =item B<__ex_mctx_save>(I) d255 3 a257 1 has to return false (C<0>). d259 1 a259 1 =item B<__ex_mctx_restored>(I) d265 1 a265 1 =item B<__ex_mctx_restore>(I) d273 3 a275 2 The default implementation (define C<__EX_MCTX_USE_SJLJ__> or as long C<__EX_MCTX_USE_CUSTOM__> is not defined) uses B jmp_buf(3): d282 4 a285 4 Alternatively one can define C<__EX_MCTX_USE_SSJLJ__> to use B sigjmp_buf(3) and C<__EX_MCTX_USE_MCSC__> to use B ucontext(3). For using a custom implementation define C<__EX_MCTX_USE_CUSTOM__> and provide own macro definitions for the four B<__ex_mctx_xxxx>. d291 2 a292 2 global exception context, returned on-the-fly by the callback "ex_ctx_t *(*B<__ex_ctx>)(void)". d294 4 a297 4 The default B<__ex_ctx> (B<__ex_ctx_default> as provided by F) returns a pointer to a static B context. For use in multi-threading environments, this should be overwritten with a per-thread context structure. d300 3 a302 2 defined: B for static initialization and B(BI) for dynamic initialization. d307 11 a317 10 B/B clauses, B calls the callback "void (*B<__ex_terminate>)(C)". It receives a pointer to the exception object which should have been thrown. The default B<__ex_terminate> (B<__ex_terminate_default> as provided by F) this prints a message of the form "C<**EX: UNCAUGHT EXCEPTION: class=0xXXXXXXXX object=0xXXXXXXXX value=0xXXXXXXX [file:123:func]>" to F and then calls abort(3) in order to terminate the application. For use in multi-threading environments, this should be overwritten with a callback function which terminates only the current thread. d321 15 a335 15 B implementation consistently uses the B, B<__ex_> and B<__EX_> prefixes for namespace protection. But at least the B prefix for the API macros B, B, B, B, B and B sometimes have a unpleasant optical appearance. Especially because B is modeled after the exception facility in B where there is no such prefix on the directives. For this B optionally provides the ability to provide additional namespace mappings for those API macros. By default (define C<__EX_NS_USE_CXX__> or as long as C<__EX_NS_USE_CUSTOM__> and C<__cplusplus> is not defined) you can additional C++ style macros named B, B, B, B and B. As an alternative you can define C<__EX_NS_USE_UCCXX__> to get the same but with an (more namespace safe) upper case first letter. d344 1 a344 1 There are two issues: which machine context to use and where to d347 1 a347 1 dispatching. d351 7 a357 4 Using B with B is straight-forward, because B 1.5 (or higher) already has support for B built-in. All which is needed is that B is configured with the Autoconf option C<--with-ex>. Then each thread has its own B exception context. d361 6 a366 6 Using B inside a standard B environment is also straight-forward, although it requires additional coding. What you basically have to do is to make sure that the B<__ex_ctx> becomes a per-thread context and that B<__ex_terminate> terminates only the current thread. To get an impression, a small utility library for this follows: d378 1 a378 1 int pthread_create_ex (pthread_t *thread, const pthread_attr_t *attr, void *(*entry)(void *), void *arg); d467 3 a469 3 Now all which is required is that you include F after the standard F header and to call B() once at startup of your program. d473 1 a473 1 As a full real-life example we will look how you can add optional Bex_use)) ? FOO_ERR_##err \ : ex_throw(&foo_ex_class, ctx, FOO_ERR_##err)) #else #define FOO_ERR(ctx,err) \ (FOO_ERR_##err) #endif d596 1 a596 2 return FOO_ERR(NULL, SYS); (*foo)->ex_use = 0; a600 12 foo_rc_t foo_ex(foo_t *foo, int use) { if (foo == NULL) return FOO_ERR(foo, ARG); #ifndef WITH_EX return FOO_ERR_IMP; #else foo->ex_use = use; return FOO_OK; #endif } d604 1 a604 1 return FOO_ERR(foo, ARG); d606 1 a606 1 return FOO_ERR(foo, XXX); d613 1 a613 1 return FOO_ERR(foo, ARG); d621 3 a623 7 compile it with C<-DWITH_EX> you build optional exception handling support into it, although it is still disabled and the library behaviour is the same. But if you now enable execptions via C the library throws exceptions where C is the C instead of returning this value. Notice that in our example we allow the enabling/disabling also on a per object basis, i.e., some B objects could have exception handling enabled and others not. d627 5 a631 2 C++ try/catch/throw language directives; setjmp(3), longjmp(3), sigsetjmp(3), siglongjmp(3). d635 10 a644 8 B was invented in January 2002 by Ralf S. Engelschall for use inside the OSSP project. Its creation was prompted by the requirement to reduce the error handling inside B. The core B/B clause is inspired by B and the implementation is derived from B 2.0.0, a similar library written 2000 by Adam M. Costello Eamc@@cs.berkeley.eduE and Cosmin Truta Ecosmin@@cs.toronto.eduE. The B clause is inspired by the B C clause. @ 1.15 log @be correct and mention version number of Pth @ text @d646 8 a653 7 B was invented in January 2002 by Ralf S. Engelschall for use inside the OSSP project. Its creation was prompted by the requirement to reduce the error handling inside B. The core B/B clause implementation is derived from B 2.0.0, a similar library written 2000 by Adam M. Costello Eamc@@cs.berkeley.eduE and Cosmin Truta Ecosmin@@cs.toronto.eduE. @ 1.14 log @remember the volatile situation @ text @d337 1 a337 1 dispatching. Here are two examples: d342 3 a344 3 already has support for B built-in. All which is needed is that B is configured with the Autoconf option C<--with-ex>. Then each thread has its own B exception context. @ 1.13 log @replace MT stuff @ text @d177 10 @ 1.12 log @forget to mention 'cleanup' in namespace mapping @ text @d331 13 a343 3 Using B with B is straight-forward. One just have to wrap pth_init() and pth_spawn() to tie the exception facility to the threading facility. d347 4 a350 1 =item F d352 89 a440 95 #ifndef __PTH_EX_H__ #define __PTH_EX_H__ #include "pth.h" int pth_init_ex (void); pth_t pth_spawn_ex (pth_attr_t attr, void *(*entry)(void *), void *arg); #ifndef PTH_EX_INTERNAL #define pth_init pth_ex_init #define pth_spawn pth_ex_spawn #endif #endif /* __PTH_EX_H__ */ =item F #include #define PTH_EX_INTERNAL #include "pth_ex.h" #include "pth.h" #include "ex.h" /* context storage key */ static pth_key_t pth_ex_ctx_key; /* context destructor */ static void pth_ex_ctx_destroy(void *data) { if (data != NULL) free(data); return; } /* callback: context fetching */ static ex_ctx_t *pth_ex_ctx(void) { return (ex_ctx_t *)pth_key_getdata(pth_ex_ctx_key); } /* callback: termination */ static void pth_ex_terminate(ex_t *e) { pth_exit(e->ex_value); } /* pth_init() wrapper */ int pth_init_ex(void) { int rc; /* perform original operation */ rc = pth_init(); /* additionally create thread data key and override OSSP ex callbacks */ pth_key_create(&pth_ex_ctx_key, pth_ex_ctx_destroy); __ex_ctx = pth_ex_ctx; __ex_terminate = pth_ex_terminate; return rc; } /* internal thread entry wrapper information */ typedef struct { void *(*entry)(void *); void *arg; } pth_spawn_ex_t; /* internal thread entry wrapper */ static void *pth_spawn_wrapper(void *arg) { pth_spawn_ex_t *wrapper; ex_ctx_t *ex_ctx; /* create per-thread exception context */ wrapper = (pth_spawn_ex_t *)arg; ex_ctx = (ex_ctx_t *)malloc(sizeof(ex_ctx_t)); EX_CTX_INITIALIZE(ex_ctx); pth_key_setdata(pth_ex_ctx_key, ex_ctx); /* perform original operation */ return wrapper->entry(wrapper->arg); } /* pth_spawn() wrapper */ pth_t pth_spawn_ex(pth_attr_t attr, void *(*entry)(void *), void *arg) { pth_spawn_ex_t wrapper; /* spawn thread but execute start function through wrapper */ wrapper.entry = entry; wrapper.arg = arg; return pth_spawn(attr, pth_spawn_wrapper, &wrapper); } d444 3 a446 5 =head2 POSIX pthreads Using B inside a B environment is identical to using it inside B. You can use the same solution as above by just replacing all B prefixes with B prefixes. @ 1.11 log @polishing after ex_cleanup introduction @ text @d303 5 a307 4 B prefix for the API macros B, B, B, B and B sometimes have a unpleasant optical appearance. Especially because B is modeled after the exception facility in B where there is no such prefix on the directives. d313 3 a315 3 named B, B, B and B. As an alternative you can define C<__EX_NS_USE_UCCXX__> to get the same but with an (more namespace safe) upper case first letter. @ 1.10 log @add ex_cleanup clause to ex_try/ex_catch, modelled after Java's "finally" @ text @d39 1 a39 1 B {...} [B {...}] B (I) {...} d45 1 a45 1 B {...}; d53 4 a56 4 B 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 making your program less robust. This is achieved d58 5 a62 5 control flow) from the location where it was 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. d69 5 a73 4 I is the exceptional return code the thrower wants to send to the catcher. All three parts are of type C internally, but every value can be used which can be (automatically) casted to a C. Exceptions are created on-the-fly by the B command. d77 1 a77 1 The B API consists of four different elements: d137 1 a137 1 =item B {...} [B {...}] B (I) {...} d140 14 a153 9 the ISO-C++ C-C construct which in turn is very similar to an ISO-C C-C construct. It consists of an B block which forms the dynamic scope of the exception handling (i.e. exceptions directly thrown there or thrown from subroutines are catched), an optional B block for performing cleanup sequences and a B block where the caught exceptions are handled. # CONTROL FLOW FIXME! d155 21 a175 19 they work only in combination. In contrast to ISO-C++ there is only one B block and not multiple ones (all B exceptions are of the same ISO-C type B). If an exception is caught, it is stored in I for inspection (or re-throwing) inside the B block. Although declared outside, the I is only valid within the B block. But it can be re-used in following B/B constructs, of course. The B block is a regular ISO-C language block, but it is not allowed to jump into it via C or C(3) or out of it via C, C or C(3) because there is some hidden setup and cleanup that needs to be done by B regardless of whether an exception is caught. Jumping into an B clause would avoid doing the setup, and jumping out of it would avoid doing the cleanup. In both cases the result is a broken exception facility. Nevertheless you are allowed to nest B clauses. The B block is a regular ISO-C language block without any restrictions. d196 1 a196 1 =item B {...} d198 3 a200 3 This directive executes its block while shielding against the throwing of exceptions, i.e., in the dynamic scope of B any B operation is ignored. d257 1 a257 1 C<__EX_MCTX_USE_CUSTOM__> is not defined) uses B jmp_buf(3): d306 1 a306 1 facility in ISO C++ where there is no such prefix on the directives. @ 1.9 log @make defaults useable from outside to reset library @ text @d39 1 a39 1 B { ... } B (I) { ... } d45 1 a45 1 B { ... }; d56 1 a56 1 code in without making your program less robust. This is achieved d136 1 a136 1 =item B { ... } B (I) { ... } d142 2 a143 1 thrown there are or from routines called from there are catched) and a d146 9 a154 7 The B and B cannot be used seperately, they work only in combination. In contrast to ISO-C++ there is only one B block and not multiple ones (all B exceptions are of the same ISO-C type B). If an exception is caught, it is stored in I for inspection (or re-throwing) inside the B block. Although declared outside, the I is only valid within the B block. But it can be re-used in following d188 1 a188 1 =item B { ... } d238 1 a238 1 cought. Usually this is a no-operation macro. @ 1.8 log @overhaul __ex_ctx and __ex_terminate stuff @ text @d265 4 a268 3 The default B<__ex_ctx> (as provided by F) returns a pointer to a static B context. For use in multi-threading environments, this should be overwritten with a per-thread context structure. d281 6 a286 6 The default B<__ex_terminate> (as provided by F) this prints a message of the form "C<**EX: UNCAUGHT EXCEPTION: class=0xXXXXXXXX object=0xXXXXXXXX value=0xXXXXXXX [file:123:func]>" to F and then calls abort(3) in order to terminate the application. For use in multi-threading environments, this should be overwritten with a callback function which terminates only the current thread. @ 1.7 log @these are boolean flags @ text @d261 7 a267 16 exception between the throw point and the catch point, B uses a global exception context macro B<__ex_ctx> holding a pointer to a structure of type B. This is a private data structure and should be treated as opaque by the user. By default (define C<__EX_CTX_USE_GLOBAL__> or as long as C<__EX_CTX_USE_CUSTOM__> is not defined) this references a global variable C<__ex_ctx_global>. This variable is either defined by the user by placing the directive "C" macro somewhere outside of any functions in the application or alternatively link the F library to the application. Alternatively one can define C<__EX_CTX_USE_STATIC__> to get a static declaration directly inside the header file (for small environments where F is included once only) or define C<__EX_CTX_USE_CUSTOM__> and provide an own macro definition for B<__ex_ctx>. d275 3 a277 3 In case there is an exception thrown which is not caught by any B/B clauses, B calls the macro B<__ex_terminate>(C). It receives a pointer to the exception d280 6 a285 9 By default (define C<__EX_TERMINATE_USE_ABORT__> or as long as C<__EX_TERMINATE_USE_CUSTOM__> is not defined) this prints a message of the form "C<**EX: UNCAUGHT EXCEPTION: class=0xXXXXXXXX object=0xXXXXXXXX value=0xXXXXXXX [file:123:func]>" to F and then calls abort(3) in order to terminate the application. Alternatively one can define C<__EX_TERMINATE_USE_NOOP__> to ignore the exception or or define C<__EX_TERMINATE_USE_CUSTOM__> and provide an own macro definition for B<__ex_terminate>. d298 4 a301 4 C<__EX_NS_USE_CXX__> o or as long as C<__EX_CTX_USE_CUSTOM__> and C<__cplusplus> is not defined) you can additional C++ style macros named B, B, B and B. As an alternative you can define C<__EX_NS_USE_UCCXX__> to get the same but with an (more d328 1 a328 2 /* include GNU pth API */ d331 4 a334 12 /* configure and include OSSP ex API */ #define __EX_CTX_USE_CUSTOM__ #define __ex_ctx \ (ex_ctx_t *)pth_getkey(ex_ctx_key) #define __ex_terminate(e) \ pth_exit(e->ex_value) #include "ex.h" int pth_init_ex(void); pth_t pth_spawn_ex(pth_attr_t attr, void *(*entry)(void *), void *arg); #ifndef PTH_EX_C d338 1 a338 1 d343 3 a345 1 #define PTH_EX_C d347 3 a349 1 d351 2 a352 2 static pth_key_t ex_ctx_key; d354 1 a354 1 void ex_ctx_destroy(void *data) d360 13 a372 1 d377 2 a378 1 d380 6 a385 1 pth_key_create(&ex_ctx_key, ex_ctx_destroy); d388 1 a388 1 d394 1 a394 1 d400 2 a401 1 d405 4 a408 2 pth_key_setdata(ex_ctx_key, ex_ctx); return wrapper.entry(wrapper.arg); d410 1 a410 1 a413 1 pth_t tid; d415 2 a416 1 d419 1 a419 2 tid = pth_spawn(attr, pth_spawn_wrapper, &wrapper); return tid; @ 1.6 log @- rename ex_shielded to ex_shielding - do not provide namespace mapping for ex_shielding - add ex_catching for testing whether someone is trying to catch exceptions @ text @d193 3 a195 3 This is a flag which can be tested inside a block to test whether the current scope is exception catching (by B somewhere in the dynamic scope) or not. d199 3 a201 3 This is a flag which can be tested inside a block to test whether the current scope is exception shielding (by B somewhere in the dynamic scope) or not. @ 1.5 log @fix license messages @ text @d47 3 a49 1 if (B) ... d191 7 a197 1 =item B d200 1 a200 1 current scope is exception shielded (by B somewhere in the d304 3 a306 4 B, B and B sometimes have a unpleasant optical appearance. Especially because B is modeled after the exception facility in ISO C++ where there is no such prefix on the directives. d311 4 a314 4 C<__cplusplus> is not defined) you can additional C++ style macros named B, B, B, B and B. As an alternative you can define C<__EX_NS_USE_UCCXX__> to get the same but with an (more namespace safe) upper case first letter. d549 1 a549 1 (( ex_shielded \ @ 1.4 log @typo @ text @d8 4 a11 4 ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License ## as published by the Free Software Foundation; either version ## 2.0 of the License, or (at your option) any later version. d13 12 a24 9 ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this file; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 ## USA, or contact the OSSP project . @ 1.3 log @many many polishing and extending @ text @d605 1 a605 1 was invented in January 2002 by Ralf S. Engelschall @ 1.2 log @notice that nesting is ok, of course @ text @d42 4 d180 52 d234 2 a235 1 =head1 EXAMPLE d237 103 d342 84 a425 1 ex_t e; d427 6 a432 1 ex_try { d434 14 a447 1 ex_throw (..., ..., 123); d450 84 a533 8 ex_catch (e) { ... if ((int)e->ex_value == 123) ... else if ((int)e->ex_value == 456) ... else ex_rethrow; d537 61 d605 6 a610 5 B was invented in January 2002 by Ralf S. Engelschall for use inside the OSSP project. Its creation was prompted by the requirement to reduce the error handling inside B. The implementation is partly derived from B 2.0.0, a similar library written 2000 by Adam M. Costello Eamc@@cs.berkeley.eduE and Cosmin Truta @ 1.1 log @Welcome OSSP ex, another new-born OSSP library. This is the first cut for a C exception handling library. Read ex.pod for details... @ text @d151 2 a152 1 cases the result is a broken exception facility. @