OSSP CVS Repository

ossp - Check-in [4996]
Not logged in
[Honeypot]  [Browse]  [Home]  [Login]  [Reports
[Search]  [Ticket]  [Timeline
  [Patchset]  [Tagging/Branching

Check-in Number: 4996
Date: 2005-Jan-30 14:58:18 (local)
2005-Jan-30 13:58:18 (UTC)
User:rse
Branch:
Comment: some cleanups to the manual page
Tickets:
Inspections:
Files:
ossp-pkg/ex/ex.pod      added-> 1.31

ossp-pkg/ex/ex.pod -> 1.31

*** /dev/null    Fri Apr 19 13:11:00 2024
--- -    Fri Apr 19 13:12:50 2024
***************
*** 0 ****
--- 1,861 ----
+ ##
+ ##  OSSP ex - Exception Handling
+ ##  Copyright (c) 2002-2005 Ralf S. Engelschall <rse@engelschall.com>
+ ##  Copyright (c) 2002-2005 The OSSP Project <http://www.ossp.org/>
+ ##  Copyright (c) 2002-2005 Cable & Wireless <http://www.cw.com/>
+ ##
+ ##  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<OSSP ex> - Exception Handling
+ 
+ =head1 VERSION
+ 
+ B<OSSP ex EX_VERSION_STR>
+ 
+ =head1 SYNOPSIS
+ 
+ B<ex_t> I<variable>;
+ 
+ B<ex_try> I<BLOCK1> [B<ex_cleanup> I<BLOCK2>] B<ex_catch> (I<variable>) I<BLOCK3>
+ 
+ B<ex_throw>(I<class>, I<object>, I<value>);
+ 
+ B<ex_rethrow>;
+ 
+ B<ex_defer> I<BLOCK>
+ 
+ B<ex_shield> I<BLOCK>
+ 
+ if (B<ex_catching>) ...
+ 
+ if (B<ex_deferred>) ...
+ 
+ if (B<ex_shielding>) ...
+ 
+ =head1 DESCRIPTION
+ 
+ B<OSSP ex> is a small B<ISO-C++> style exception handling library for
+ use in the B<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 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<OSSP ex> exception is a triple
+ E<lt>I<class>,I<object>,I<value>E<gt> where I<class> identifies the
+ class of the exception thrower, I<object> identifies the particular
+ class instance of the exception thrower, and I<value> is the exceptional
+ return code value the thrower wants to communicate. All three parts are
+ of type "C<void *>" internally, but every value which can be lossless
+ "casted" to this type is usable. Exceptions are created on-the-fly by
+ the B<ex_throw> command.
+ 
+ =head2 APPLICATION PROGRAMMER INTERFACE (API)
+ 
+ The B<OSSP ex> API consists of the following elements:
+ 
+ =over 4
+ 
+ =item B<ex_t> I<variable>;
+ 
+ This is the declaration of an exception variable. It is usually never
+ initialized manually. Instead it is initialized by an B<ex_catch> clause
+ and just used read-only inside its block. Such a variable of type
+ B<ex_t> consists of six attributes:
+ 
+ =over 2
+ 
+ =item C<void *>I<ex_class>
+ 
+ This is the I<class> argument of the B<ex_throw> call which created
+ the exception. This can globally and uniquely identify the class to
+ which I<ex_value> 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<ex_value>. It
+ is usually just an additional (optional) information to I<ex_value>.
+ 
+ =item C<void *>I<ex_object>
+ 
+ This is the I<object> argument of the B<ex_throw> call which created the
+ exception. This can globally and uniquely identify the class instance
+ I<ex_value> 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<ex_value>.
+ 
+ =item C<void *>I<ex_value>
+ 
+ This is the I<value> argument of the B<ex_throw> 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 C<return>ed 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 C<char *>I<ex_file>
+ 
+ This is the file name of the B<ISO-C> source where the B<ex_throw> 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<int> I<ex_line>
+ 
+ This is the line number inside the B<ISO-C> source file name where the
+ B<ex_throw> 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<char *>I<ex_func>
+ 
+ This is the function name (if determinable, else "C<#NA#>") inside the
+ B<ISO-C> source file name where the B<ex_throw> 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<ex_try> I<BLOCK1> [B<ex_cleanup> I<BLOCK2>] B<ex_catch> (I<variable>) I<BLOCK3>
+ 
+ This is the primary syntactical construct provided by B<OSSP ex>. It
+ is modeled after the B<ISO-C++> C<try>-C<catch> clause which in turn
+ is very similar to an B<ISO-C> C<if>-C<else> clause. It consists of an
+ B<ex_try> block I<BLOCK1> 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<ex_cleanup> block I<BLOCK2> for
+ performing cleanup operations and an B<ex_catch> block I<BLOCK3> where
+ the caught exceptions are handled.
+ 
+ The control flow in case no exception is thrown is simply I<BLOCK1>,
+ optionally followed by I<BLOCK2>; I<BLOCK3> is skipped. The control
+ flow in case an exception is thrown is: I<BLOCK1> (up to the statement
+ where the exception is thrown only), optionally followed by I<BLOCK2>,
+ followed by I<BLOCK3>.
+ 
+ The B<ex_try>, B<ex_cleanup> and B<ex_catch> cannot be used separately,
+ they work only in combination because they form a language clause
+ as a whole. In contrast to B<ISO-C++> there is only one B<ex_catch>
+ block and not multiple ones (all B<OSSP ex> exceptions are of the same
+ B<ISO-C> type B<ex_t>). If an exception is caught, it is stored in
+ I<variable> for inspection inside the B<ex_catch> block. Although having
+ to be declared outside, the I<variable> value is only valid within
+ the B<ex_catch> block. But the variable can be re-used in subsequent
+ B<ex_catch> clauses, of course.
+ 
+ The B<ex_try> block is a regular B<ISO-C> language statement block, but
+ it is not allowed to jump into it via "C<goto>" or C<longjmp>(3) or out
+ of it via "C<break>", "C<return>", "C<goto>" or C<longjmp>(3) because there
+ is some hidden setup and cleanup that needs to be done by B<OSSP ex>
+ regardless of whether an exception is caught. Jumping into an B<ex_try>
+ 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<ex_try>
+ clauses.
+ 
+ The B<ex_cleanup> and B<ex_catch> blocks are regular B<ISO-C> language
+ statement blocks without any restrictions. You are even allowed to throw
+ (and in the B<ex_catch> block to re-throw) an exception.
+ 
+ There is just one subtle portability detail you have to remember about
+ B<ex_try> blocks: all accessible B<ISO-C> objects have the (expected)
+ values as of the time B<ex_throw> was called, except that the values
+ of objects of automatic storage invocation duration that do not have
+ the "C<volatile>" storage class I<and> have been changed between the
+ B<ex_try> invocation and B<ex_throw> are indeterminate. This is because
+ both you usually do not know which commands in the B<ex_try> were
+ already successful before the exception was thrown (logically speaking)
+ and because the underlying B<ISO-C> setjmp(3) facility applies those
+ restrictions (technically speaking).
+ 
+ =item B<ex_throw>(I<class>, I<object>, I<value>);
+ 
+ This builds an exception from the supplied arguments and throws it.
+ If an B<ex_try>/B<ex_catch> clause formed the dynamic scope of the
+ B<ex_throw> call, this exception is copied into the I<variable> of
+ its B<ex_catch> clause and the program control flow is continued in
+ the (optional B<ex_cleanup> and then in the) B<ex_catch> block. If
+ no B<ex_try>/B<ex_catch> clause exists in the dynamic scope of the
+ B<ex_throw> call, the program calls C<abort>(3). The B<ex_throw> can
+ be performed everywhere, including inside B<ex_try>, B<ex_cleanup> and
+ B<ex_catch> blocks.
+ 
+ =item B<ex_rethrow>;
+ 
+ This is only valid within an B<ex_catch> block and re-throws
+ the current exception (in I<variable>). It is similar to the
+ call B<ex_throw>(I<variable>.ex_class, I<variable>.ex_object,
+ I<variable>.ex_value) except for the difference that the C<ex_file>,
+ C<ex_line> and C<ex_func> elements of the caught exception are passed
+ through as it would have been never caught.
+ 
+ =item B<ex_defer> I<BLOCK>
+ 
+ This directive executes I<BLOCK> while deferring the throwing of
+ exceptions, i.e., inside the dynamic scope of B<ex_defer> all
+ B<ex_throw> operations are remembered but deferred and on leaving the
+ I<BLOCK> the I<first> occurred exception is thrown. The second and
+ subsequent exceptions are ignored.
+ 
+ The B<ex_defer> block I<BLOCK> is a regular B<ISO-C> language statement
+ block, but it is not allowed to jump into it via "C<goto>" or
+ C<longjmp>(3) or out of it via "C<break>", "C<return>", "C<goto>" or
+ C<longjmp>(3) because this would cause the deferral level to become out
+ of sync. Jumping into an B<ex_defer> 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<ex_defer> clauses.
+ 
+ =item B<ex_shield> I<BLOCK>
+ 
+ This directive executes I<BLOCK> while shielding it against the throwing
+ of exceptions, i.e., inside the dynamic scope of B<ex_shield> all
+ B<ex_throw> operations are just silently ignored.
+ 
+ The B<ex_shield> block is a regular B<ISO-C> language statement block,
+ but it is not allowed to jump into it via "C<goto>" or C<longjmp>(3)
+ or out of it via "C<break>", "C<return>", "C<goto>" or C<longjmp>(3)
+ because this would cause the shielding level to become out of sync.
+ Jumping into an B<ex_shield> 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<ex_shield> clauses.
+ 
+ =item B<ex_catching>
+ 
+ This is a boolean flag which can be checked inside the dynamic scope
+ of an B<ex_try> clause to test whether the current scope is exception
+ catching (see B<ex_try>/B<ex_catch> clause).
+ 
+ =item B<ex_deferred>
+ 
+ This is a boolean flag which can be checked inside the dynamic scope of
+ an B<ex_defer> clause to test whether the current scope is exception
+ deferring (see B<ex_defer> clause).
+ 
+ =item B<ex_shielding>
+ 
+ This is a boolean flag which can be checked inside the dynamic scope of
+ an B<ex_shield> clause to test whether the current scope is exception
+ shielding (see B<ex_shield> clause).
+ 
+ =back
+ 
+ =head1 IMPLEMENTATION CONTROL
+ 
+ B<OSSP ex> uses a very light-weight but still flexible exception
+ facility implementation. The following adjustments can be made before
+ including the F<ex.h> header:
+ 
+ =head2 Machine Context
+ 
+ In order to move the program control flow from the exception throw point
+ (B<ex_throw>) to the catch point (B<ex_catch>), B<OSSP ex> 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<mctx>.
+ 
+ =item B<__ex_mctx_save>(__ex_mctx_struct *I<mctx>)
+ 
+ This is called by the prolog of B<ex_try> to save the current machine
+ context in I<mctx>. 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<mctx>)
+ 
+ This is called by the epilog of B<ex_try> 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<mctx>)
+ 
+ This is called by B<ex_throw> at the old machine context in order to
+ restore the machine context of the B<ex_try>/B<ex_catch> 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<ISO-C> 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<POSIX.1>
+ sigjmp_buf(3) or C<__EX_MCTX_MCSC__> to use B<POSIX.1> 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<OSSP ex> uses a
+ global exception context, returned on-the-fly by the callback "C<ex_ctx_t
+ *(*>B<__ex_ctx>C<)(void)>".
+ 
+ By default, B<__ex_ctx> (which is B<__ex_ctx_default> as provided by
+ F<libex>) returns a pointer to a static C<ex_ctx_t> context. For use in
+ multi-threading environments, this should be overwritten with a
+ callback function returning a per-thread context structure (see section
+ B<MULTITHREADING ENVIRONMENTS> below).
+ 
+ To initialize an exception context structure there are two macros
+ defined: "B<EX_CTX_INITIALIZER>" for static initialization and
+ "C<void >B<EX_CTX_INITIALIZE>C<(ex_ctx_t *)>" for dynamic
+ initialization.
+ 
+ =head2 Termination Handler
+ 
+ In case there is an exception thrown which is not caught by any
+ B<ex_try>/B<ex_catch> clauses, B<OSSP ex> calls the callback "C<void
+ (*>B<__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<libex>) prints a message of the form "C<**EX:
+ UNCAUGHT EXCEPTION: class=0xXXXXXXXX object=0xXXXXXXXX value=0xXXXXXXX
+ [xxxx:NNN:xxxx]>" to F<stderr> 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<ex_try>/B<ex_catch> clause in its "C<main()>" in order to
+ more gracefully terminate the application.
+ 
+ =head2 Namespace Mapping
+ 
+ The B<OSSP ex> implementation consistently uses the "C<ex_>", "C<__ex_>"
+ and "C<__EX_>" prefixes for namespace protection. But at least
+ the "C<ex_>" prefix for the API macros B<ex_try>, B<ex_cleanup>,
+ B<ex_catch>, B<ex_throw>, B<ex_rethrow> and B<ex_shield> sometimes have
+ an unpleasant optical appearance. Especially because B<OSSP ex> is
+ modeled after the exception facility of B<ISO-C++> where there is no
+ such prefix on the language directives, of course.
+ 
+ For this, B<OSSP ex> 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<ISO-C++> style names
+ B<catch>, B<cleanup>, B<throw>, B<rethrow> and B<shield>. 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<ex_cleanup> or B<ex_catch> clauses
+ must be declared before the B<ex_try> clause, otherwise they only
+ exist inside the B<ex_try> block. In the example above, C<cp1>, C<cp2>
+ and C<cp3> are automatic variables and only exist in the block of the
+ B<ex_try> clause, the code in the B<ex_cleanup> and B<ex_catch> clauses
+ does not know anything about them.
+ 
+ =item B<02: variable initialization>
+ 
+ Variables which are used in the B<ex_cleanup> or B<ex_catch> clauses must
+ be initialized before the point of the first possible B<ex_throw> is
+ reached. In the example above, B<ex_cleanup> would have trouble using
+ C<cp3> if mallocex() throws a exception when allocating a C<TOOBIG>
+ buffer.
+ 
+ =item B<03: volatile variables>
+ 
+ Variables which are used in the B<ex_cleanup> or B<ex_catch> clauses
+ must be declared with the storage class "C<volatile>", otherwise they
+ might contain outdated information if B<ex_throw> throws an exception.
+ If using a "free if unset" approach like the example does in the
+ B<ex_cleanup> clause, the variables must be initialized (see B<02>) I<and>
+ remain valid upon use.
+ 
+ =item B<04: clean before catch>
+ 
+ The B<ex_cleanup> clause is not only written down before the B<ex_catch>
+ clause, it is also evaluated before the B<ex_catch> clause. So,
+ resources being cleaned up must no longer be used in the B<ex_catch>
+ 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<ex_try>/B<ex_cleanup>/B<ex_catch> 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<cp1> in the B<ex_cleanup> clause if mallocex() throws an exception if
+ allocating a C<TOOBIG> buffer. The C<globalcontext-&gt;first> 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<OSSP ex> 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<OSSP ex> to work correctly in a
+ multi-threading environment like B<POSIX pthreads> or B<GNU pth>.
+ 
+ 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<OSSP ex> together with B<GNU pth> is straight-forward, because
+ B<GNU pth> 2.0 (and higher) already has support for B<OSSP ex> built-in.
+ All which is needed is that B<GNU pth> is configured with the B<GNU
+ Autoconf> option C<--with-ex>. Then each B<GNU pth> user-space thread
+ has its own B<OSSP ex> exception context automatically. The default of
+ using B<ISO-C> jmp_buf(3) does not conflict with the thread dispatching
+ mechanisms used by B<GNU pth>.
+ 
+ =head2 POSIX pthreads
+ 
+ Using B<OSSP ex> inside an arbitrary B<POSIX pthreads> 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<pthread_ex.h>
+ 
+  #ifndef __PTHREAD_EX_H__
+  #define __PTHREAD_EX_H__
+ 
+  #include <pthread.h>
+ 
+  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<pthread_ex.c>
+ 
+  #include <stdlib.h>
+  #include <pthread.h>
+ 
+  #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);
+  }
+ 
+ =back
+ 
+ Now all which is required is that you include F<pthread_ex.h> after the
+ standard F<pthread.h> header and to call B<pthread_init> once at startup
+ of your program.
+ 
+ =head1 EXAMPLES
+ 
+ As a real-life example we will look how you can add optional B<OSSP
+ ex> based exception handling support to a library B<foo>. The original
+ library looks like this:
+ 
+ =over 2
+ 
+ =item F<foo.h>
+ 
+  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<foo.c>
+ 
+  #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<foo.h>
+ 
+  ...
+  extern const char foo_id[];
+  ...
+ 
+ =item F<foo.c>
+ 
+  #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<ex_value> is
+ the C<foo_rc_t> instead of returning this value.
+ 
+ =head1 SEE ALSO
+ 
+ B<ISO-C++> C<try>, C<catch>, C<throw>.
+ 
+ B<Java> C<try>, C<catch>, C<finally>, C<throw>.
+ 
+ B<ISO-C> jmp_buf(3), setjmp(3), longjmp(3).
+ 
+ B<POSIX.1> sigjmp_buf(3), sigsetjmp(3), siglongjump(3).
+ 
+ B<POSIX.1> ucontext(3), setcontext(3), getcontext(3).
+ 
+ =head1 HISTORY
+ 
+ B<OSSP ex> was invented in January 2002 by Ralf S. Engelschall
+ E<lt>rse@engelschall.comE<gt> for use inside the B<OSSP> project. Its
+ creation was prompted by the requirement to reduce the error handling
+ inside B<OSSP lmtp2nntp>.
+ 
+ The core B<try>/B<catch> clause was inspired by B<ISO-C++> and the
+ implementation was partly derived from B<cexcept> 2.0.0, a similar
+ library written 2000 by Adam M. Costello E<lt>amc@cs.berkeley.eduE<gt>
+ and Cosmin Truta E<lt>cosmin@cs.toronto.eduE<gt>.
+ 
+ The B<cleanup> clause was inspired by the B<Java> C<finally> clause.
+ The B<shield> feature was inspired by an "C<errno>" shielding facility
+ used in the B<GNU pth> implementation. The B<defer> 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
+ 

CVSTrac 2.0.1