OSSP CVS Repository

ossp - Difference in ossp-pkg/ex/ex.pod versions 1.22 and 1.23
Not logged in
[Honeypot]  [Browse]  [Home]  [Login]  [Reports
[Search]  [Ticket]  [Timeline
  [History

ossp-pkg/ex/ex.pod 1.22 -> 1.23

--- 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-&gt;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

CVSTrac 2.0.1