Index: ossp-pkg/ex/ChangeLog RCS File: /v/ossp/cvs/ossp-pkg/ex/ChangeLog,v rcsdiff -q -kk '-r1.4' '-r1.5' -u '/v/ossp/cvs/ossp-pkg/ex/ChangeLog,v' 2>/dev/null --- ChangeLog 2003/01/06 15:31:24 1.4 +++ ChangeLog 2003/01/30 10:23:12 1.5 @@ -11,7 +11,12 @@ This is the list of all changes to the OSSP ex source tree. - Changes between 0.9.1 and 1.0.0 (08-Mar-2002 to 06-Jan-2003) + Changes between 1.0.1 and 1.0.2 (06-Jan-2003 to 30-Jan-2003) + + *) Polished and enhanced the manual page ex(3). + [Ralf S. Engelschall] + + Changes between 1.0.0 and 1.0.1 (08-Mar-2002 to 06-Jan-2003) *) Adjusted copyright messages to include new year 2003. [Ralf S. Engelschall] Index: ossp-pkg/ex/README RCS File: /v/ossp/cvs/ossp-pkg/ex/README,v rcsdiff -q -kk '-r1.8' '-r1.9' -u '/v/ossp/cvs/ossp-pkg/ex/README,v' 2>/dev/null --- README 2003/01/06 15:32:27 1.8 +++ README 2003/01/30 10:23:12 1.9 @@ -22,6 +22,13 @@ the exceptional return codes from sub-routines are correctly passed back to the parent. + The OSSP ex facility also provides advanced exception handling + features like shielded and deferred exceptions. Additionally, OSSP ex + allows you to choose the used underlying machine context switching + facility (jmp_buf(3), sigjmp_buf(3), ucontext(3), etc) and optionally + support multi-threading environments by allowing you to store the + exception catching stack in a thread-safe way. + COPYRIGHT AND LICENSE Copyright (c) 2002-2003 Ralf S. Engelschall Index: ossp-pkg/ex/ex.pod RCS File: /v/ossp/cvs/ossp-pkg/ex/ex.pod,v rcsdiff -q -kk '-r1.26' '-r1.27' -u '/v/ossp/cvs/ossp-pkg/ex/ex.pod,v' 2>/dev/null --- ex.pod 2003/01/06 15:31:24 1.26 +++ ex.pod 2003/01/30 10:23:12 1.27 @@ -79,8 +79,8 @@ 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 is useable which can -be lossless casted this type. Exceptions are created on-the-fly by the +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. =head2 APPLICATION PROGRAMMER INTERFACE (API) @@ -101,19 +101,20 @@ =item CI This is the I argument of the B call which created -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. +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 should globally and uniquely identify the class -instance of I (in case multiple instances exists at all). +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 -information to I. +(optional) information to I. =item CI @@ -127,22 +128,23 @@ =item CI This is the file name of the B 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. +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 provided as an additional -information about the throw point and is intended mainly for tracing and -debugging purposes. +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 provided as an additional information about the throw point and is -intended mainly for tracing and debugging purposes. +is automatically provided as an additional information about the throw +point and is intended mainly for tracing and debugging purposes. =back @@ -174,8 +176,8 @@ 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 +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 @@ -191,7 +193,7 @@ 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 +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) @@ -202,11 +204,12 @@ 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 -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 +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; @@ -222,50 +225,51 @@ This directive executes I while deferring the throwing of exceptions, i.e., inside the dynamic scope of B all -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. +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 silently ignored. +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. +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. +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. +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. +shielding (see B clause). =back @@ -311,7 +315,7 @@ =back -The default implementation (define C<__EX_MCTX_SJLJ__> or as long +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: @@ -334,8 +338,9 @@ 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. +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 @@ -347,15 +352,17 @@ 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 should have been thrown. +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. +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 @@ -395,7 +402,7 @@ strcpy(cp1, "foo"); strcpy(cp2, "bar"); } - ex_clean { + ex_cleanup { if (cp3 != NULL) free(cp3); if (cp2 != NULL) free(cp2); if (cp1 != NULL) free(cp1); @@ -411,59 +418,59 @@ =item B<01: variable scope> -Variables which are used in the B or B clauses +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 +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 +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 +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 +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 +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 +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 free(3)'d +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 +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 +C in the B clause if mallocex() throws an exception if allocating a C buffer. The C pointer hence becomes invalid. =back -As following is fixed version of the code (annotated with the pitfall +The following is fixed version of the code (annotated with the pitfall items for reference): /* GOOD EXAMPLE */ { /*01*/ - volatile /*03*/ char *cp1 = NULL /*02*/; - volatile /*03*/ char *cp2 = NULL /*02*/; - volatile /*03*/ char *cp3 = NULL /*02*/; + 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; @@ -473,8 +480,8 @@ strcpy(cp1, "foo"); strcpy(cp2, "bar"); } - clean { - /*04*/ printf("cp3=%s", cp3 == NULL /*02*/ ? "" : cp3); + clean { /*04*/ + printf("cp3=%s", cp3 == NULL /*02*/ ? "" : cp3); if (cp3 != NULL) free(cp3); if (cp2 != NULL) @@ -487,13 +494,13 @@ } } -An alternative fixed version could also be: +Alternatively, this could also be used: - /* ALTERNATE *05* GOOD EXAMPLE */ + /* ALTERNATIVE GOOD EXAMPLE */ { /*01*/ - volatile /*03*/ char *cp1 = NULL /*02*/; - volatile /*03*/ char *cp2 = NULL /*02*/; - volatile /*03*/ char *cp3 = NULL /*02*/; + 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; @@ -503,8 +510,8 @@ strcpy(cp1, "foo"); strcpy(cp2, "bar"); } - clean { - /*04*/ printf("cp3=%s", cp3 == NULL /*02*/ ? "" : cp3); + clean { /*04*/ + printf("cp3=%s", cp3 == NULL /*02*/ ? "" : cp3); if (cp3 != NULL) free(cp3); if (cp2 != NULL) @@ -518,7 +525,6 @@ } } - =head1 MULTITHREADING ENVIRONMENTS B is designed to work both in single-threading and @@ -534,12 +540,12 @@ =head2 GNU pth Using B together with B is straight-forward, because -B 1.5 (and higher) already has support for B built-in. +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. +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 @@ -550,7 +556,7 @@ terminates only the current thread. To get an impression, a small utility library for this follows: -=over 4 +=over 2 =item F @@ -560,7 +566,8 @@ #include int pthread_init_ex (void); - int pthread_create_ex (pthread_t *, const pthread_attr_t *, void *(*)(void *), void *); + int pthread_create_ex (pthread_t *, const pthread_attr_t *, + void *(*)(void *), void *); #ifndef PTHREAD_EX_INTERNAL #define pthread_init pthread_init_ex @@ -592,7 +599,8 @@ /* callback: context fetching */ static ex_ctx_t *pthread_ex_ctx(void) { - return (ex_ctx_t *)pthread_getspecific(pthread_ex_ctx_key); + return (ex_ctx_t *) + pthread_getspecific(pthread_ex_ctx_key); } /* callback: termination */ @@ -606,8 +614,10 @@ { int rc; - /* additionally create thread data key and override OSSP ex callbacks */ - pthread_key_create(&pthread_ex_ctx_key, pthread_ex_ctx_destroy); + /* 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; @@ -637,14 +647,18 @@ } /* pthread_create() wrapper */ - int pthread_create_ex(pthread_t *thread, const pthread_attr_t *attr, void *(*entry)(void *), void *arg) + 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 */ + /* spawn thread but execute start + function through wrapper */ wrapper.entry = entry; wrapper.arg = arg; - return pthread_create(thread, attr, pthread_create_wrapper, &wrapper); + return pthread_create(thread, attr, + pthread_create_wrapper, &wrapper); } =back @@ -748,7 +762,7 @@ You can achieve this very easily by changing the library as following: -=over 4 +=over 2 =item F @@ -832,7 +846,7 @@ 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 +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.