Index: ossp-pkg/ex/00TODO RCS File: /v/ossp/cvs/ossp-pkg/ex/00TODO,v rcsdiff -q -kk '-r1.3' '-r1.4' -u '/v/ossp/cvs/ossp-pkg/ex/00TODO,v' 2>/dev/null --- 00TODO 2002/03/07 21:30:09 1.3 +++ 00TODO 2002/03/30 18:55:28 1.4 @@ -1,90 +0,0 @@ - -**** IMPROVE DOCUMENTATION **** - -/* BAD EXAMPLE */ -try { - char *cp1, *cp2, cp3; - - cp1 = mallocex(SMALLAMOUNT); - globalcontext->first = cp1; - cp2 = mallocex(TOOBIG); - cp3 = mallocex(SMALLAMOUNT); - - strcpy(cp1, "foo"); strcpy(cp2, "bar"); -} -clean { - if (cp3 != NULL) free(cp3); - if (cp2 != NULL) free(cp2); - if (cp1 != NULL) free(cp1); -} -catch(ex) { - printf("cp3=%s", cp3); - rethrow; -} - -The code above shows some pitfalls and contains many errors. - -01) variable scope - -Variables which are used in the "clean" or "catch" blocks must be declared -before "try", otherwise they only exists inside the "try" block. In the -example above, cp1-3 are automatic variables and only exist in the "try" -block, the "clean" and "catch" blocks don't know anything about them. - -02) variable initialization - -Variables which are used in the "clean" or "catch" blocks must be initialized -before the point of the first possible "throw" is reached. In the example -above, "clean" would have trouble using cp3 when mallocex() throws a exception -when allocating a TOOBIG buffer. - -03) volatile variables - -Variables which are used in the "clean" or "catch" blocks must be declared -volatile, otherwise they might contain outdated information when "throw" does -the longjmp(). When using a "free if unset" mechanism like the example does in -the "clean" block, the variables must be initialized (see 02) *and* remain -valid upon use. - -04) clean before catch - -The "clean" block is evaluated before "catch", so resources being cleaned up -must no longer be used in the "catch" block. The example above would have -trouble referencing the character strings in the printf() statement because -these have been free()d before. - -05) variable uninitialization - -When resources are passed away and out of the scope of the "try/clean/catch" -construct and the variables were initialized for using a "free if unset" -mechanism then they must be uninitialized after being passed away. The -example above would free() cp1 in the "clean" block when mallocex() throws a -exception when allocating a TOOBIG buffer. The globalcontext->first pointer -hence becomes invalid. - -/* GOOD EXAMPLE */ | /* ALTERNATE *05* GOOD EXAMPLE */ -{ /*01*/ | { /*01*/ - volatile /*03*/ char *cp1 = NULL /*02*/; | volatile /*03*/ char *cp1 = NULL /*02*/; - volatile /*03*/ char *cp2 = NULL /*02*/; | volatile /*03*/ char *cp2 = NULL /*02*/; - volatile /*03*/ char *cp3 = NULL /*02*/; | volatile /*03*/ char *cp3 = NULL /*02*/; - try { | try { - cp1 = mallocex(SMALLAMOUNT); | cp1 = mallocex(SMALLAMOUNT); - globalcontext->first = cp1; | globalcontext->first = cp1; - cp1 = NULL /*05 give away*/; | /*05 keep responsibility*/ - cp2 = mallocex(TOOBIG); | cp2 = mallocex(TOOBIG); - cp3 = mallocex(SMALLAMOUNT); | cp3 = mallocex(SMALLAMOUNT); - | - strcpy(cp1, "foo"); strcpy(cp2, "bar"); | strcpy(cp1, "foo"); strcpy(cp2, "bar"); - } | } - clean { | clean { - /*04*/ printf("cp3=%s", cp3 == NULL /*02*/ ? "" : cp3); | /*04*/ printf("cp3=%s", cp3 == NULL /*02*/ ? "" : cp3); - if (cp3 != NULL) free(cp3); | if (cp3 != NULL) free(cp3); - if (cp2 != NULL) free(cp2); | if (cp2 != NULL) free(cp2); - /*05 cp1 was given away */ | if (cp1 != NULL) free(cp1); - } | } - catch(ex) { | catch(ex) { - /*05 global context untouched */ | globalcontext->first = NULL; - rethrow; } | rethrow; - } | } -} | } - Index: ossp-pkg/ex/ex.pod RCS File: /v/ossp/cvs/ossp-pkg/ex/ex.pod,v rcsdiff -q -kk '-r1.22' '-r1.23' -u '/v/ossp/cvs/ossp-pkg/ex/ex.pod,v' 2>/dev/null --- 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 ## ## 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 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 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/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() +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 +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 is designed to work both in single-threading and