Index: ossp-pkg/ex/00TODO RCS File: /v/ossp/cvs/ossp-pkg/ex/00TODO,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/ex/00TODO,v' | diff -u /dev/null - -L'ossp-pkg/ex/00TODO' 2>/dev/null --- ossp-pkg/ex/00TODO +++ - 2024-05-05 02:41:01.080633842 +0200 @@ -0,0 +1,90 @@ + +**** 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; + } | } +} | } +