--- ex.pod 2002/03/30 18:15:49 1.22
+++ ex.pod 2002/03/30 18:55:28 1.23
@@ -5,7 +5,7 @@
## Copyright (c) 2002 Cable & Wireless Deutschland <http://www.cw.com/de/>
##
## This file is part of OSSP ex, an exception library
-## which can be found at http://www.ossp.org/pkg/ex/.
+## 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
@@ -375,6 +375,150 @@
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_clean {
+ 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_clean> 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_clean> and B<ex_catch> clauses
+does not know anything about them.
+
+=item B<02: variable initialization>
+
+Variables which are used in the B<ex_clean> 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_clean> 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_clean> 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_clean> clause, the variables must be initialized (see B<02>) I<and>
+remain valid upon use.
+
+=item B<04: clean before catch>
+
+The B<ex_clean> 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 printf3) statement because these have been free()d
+before.
+
+=item B<05: variable uninitialization>
+
+If resources are passed away and out of the scope of the
+B<ex_try>/B<ex_clean>/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()
+C<cp1> in the B<ex_clean> clause if mallocex() throws an exception if
+allocating a C<TOOBIG> buffer. The C<globalcontext->first> pointer
+hence becomes invalid.
+
+=back
+
+As 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*/;
+ 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;
+ }
+ }
+
+An alternative fixed version could also be:
+
+ /* ALTERNATE *05* GOOD EXAMPLE */
+ { /*01*/
+ volatile /*03*/ char *cp1 = NULL /*02*/;
+ volatile /*03*/ char *cp2 = NULL /*02*/;
+ volatile /*03*/ char *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
|