OSSP CVS Repository

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

Check-in Number: 1972
Date: 2002-Mar-07 22:30:09 (local)
2002-Mar-07 21:30:09 (UTC)
User:rse
Branch:
Comment: Implement one of the coolest things since sliced bread: deferred exceptions. This allows one to more conviniently program the allocation (and the freeing in case of an error) of multiple (independent) resources.
Tickets:
Inspections:
Files:
ossp-pkg/ex/00TODO      1.2 -> 1.3     0 inserted, 25 deleted
ossp-pkg/ex/ex.h      1.16 -> 1.17     28 inserted, 6 deleted
ossp-pkg/ex/ex.pod      1.20 -> 1.21     30 inserted, 1 deleted
ossp-pkg/ex/ex_test.c      1.8 -> 1.9     39 inserted, 0 deleted

ossp-pkg/ex/00TODO 1.2 -> 1.3

--- 00TODO       2002/03/07 15:01:30     1.2
+++ 00TODO       2002/03/07 21:30:09     1.3
@@ -1,29 +1,4 @@
 
-Idea: deferred exceptions:
-o ex_defer BLOCK
-o if (ex_deferred) ...
-
-to avoid having to initialize every variable in this context:
-
-char *cp1;
-char *cp2;
-char *cp3;
-ex_try {
-    ex_defer {
-        cp1 = mymalloc(...);
-        cp2 = mymalloc(...);
-        cp3 = mymalloc(...);
-    }
-}
-ex_cleanup {
-    if (cp1 != NULL)
-        free(cp1);
-    if (cp2 != NULL)
-        free(cp2);
-    if (cp3 != NULL)
-        free(cp3);
-}
-
 **** IMPROVE DOCUMENTATION ****
 
 /* BAD EXAMPLE */


ossp-pkg/ex/ex.h 1.16 -> 1.17

--- ex.h 2002/02/25 10:33:02     1.16
+++ ex.h 2002/03/07 21:30:09     1.17
@@ -94,6 +94,9 @@
 /* declare the context type (private) */
 typedef struct {
     __ex_mctx_t  *ctx_mctx;     /* permanent machine context of enclosing try/catch */
+    int           ctx_deferred; /* permanent flag whether exception is deferred */
+    int           ctx_deferring;/* permanent counter of exception deferring level */
+    int           ctx_defer;    /* temporary flag for exception deferring macro */
     int           ctx_shielding;/* permanent counter of exception shielding level */
     int           ctx_shield;   /* temporary flag for exception shielding macro */
     int           ctx_caught;   /* temporary flag whether exception was caught */
@@ -103,10 +106,13 @@
 
 /* the static and dynamic initializers for a context structure */
 #define EX_CTX_INITIALIZER \
-    { NULL, 0, 0, 0, 0, { NULL, NULL, NULL, NULL, 0, NULL } }
+    { NULL, 0, 0, 0, 0, 0, 0, 0, { NULL, NULL, NULL, NULL, 0, NULL } }
 #define EX_CTX_INITIALIZE(ctx) \
     do { \
         (ctx)->ctx_mctx         = NULL; \
+        (ctx)->ctx_deferred     = 0;    \
+        (ctx)->ctx_deferring    = 0;    \
+        (ctx)->ctx_defer        = 0;    \
         (ctx)->ctx_shielding    = 0;    \
         (ctx)->ctx_shield       = 0;    \
         (ctx)->ctx_caught       = 0;    \
@@ -178,20 +184,24 @@
 
 /* the throwing of a new exception */
 #define ex_throw(c,o,v) \
-    (__ex_ctx()->ctx_shielding > 0 ? 0 : \
+    ((   __ex_ctx()->ctx_shielding > 0 \
+      || (__ex_ctx()->ctx_deferring > 0 && __ex_ctx()->ctx_deferred == 1)) ? 0 : \
      (__ex_ctx()->ctx_ex.ex_class  = (void *)(c), \
       __ex_ctx()->ctx_ex.ex_object = (void *)(o), \
       __ex_ctx()->ctx_ex.ex_value  = (void *)(v), \
       __ex_ctx()->ctx_ex.ex_file   = __FILE__, \
       __ex_ctx()->ctx_ex.ex_line   = __LINE__, \
       __ex_ctx()->ctx_ex.ex_func   = __EX_FUNC__, \
-      (  __ex_ctx()->ctx_mctx == NULL \
-       ? (__ex_terminate((ex_t *)&(__ex_ctx()->ctx_ex)), -1) \
-       : (__ex_mctx_restore(__ex_ctx()->ctx_mctx), 1) )))
+      __ex_ctx()->ctx_deferred     = 1, \
+      (__ex_ctx()->ctx_deferring > 0 ? 0 : \
+       (__ex_ctx()->ctx_mctx == NULL \
+        ? (__ex_terminate((ex_t *)&(__ex_ctx()->ctx_ex)), -1) \
+        : (__ex_mctx_restore(__ex_ctx()->ctx_mctx), 1) ))))
 
 /* the re-throwing of an already caught exception */
 #define ex_rethrow \
-    (__ex_ctx()->ctx_shielding > 0 ? 0 : \
+    ((   __ex_ctx()->ctx_shielding > 0 \
+      || __ex_ctx()->ctx_deferring > 0) ? 0 : \
       (  __ex_ctx()->ctx_mctx == NULL \
        ? (__ex_terminate((ex_t *)&(__ex_ctx()->ctx_ex)), -1) \
        : (__ex_mctx_restore(__ex_ctx()->ctx_mctx), 1) ))
@@ -204,11 +214,21 @@
          __ex_ctx()->ctx_shield =  0, \
          __ex_ctx()->ctx_shielding--)
 
+/* defer immediate exception handling */
+#define ex_defer \
+    for (((__ex_ctx()->ctx_deferring)++ == 0 ? __ex_ctx()->ctx_deferred = 0 : 0), \
+         __ex_ctx()->ctx_defer =  1;  \
+         __ex_ctx()->ctx_defer == 1;  \
+         __ex_ctx()->ctx_defer =  0,  \
+         ((--(__ex_ctx()->ctx_deferring) == 0 && __ex_ctx()->ctx_deferred == 1) ? ex_rethrow : 0))
+
 /* exception handling tests */
 #define ex_catching \
     (__ex_ctx()->ctx_mctx != NULL)
 #define ex_shielding \
     (__ex_ctx()->ctx_shielding > 0)
+#define ex_deferring \
+    (__ex_ctx()->ctx_deferring > 0)
 
 /* optional namespace mapping */
 #if defined(__EX_NS_UCCXX__)
@@ -218,6 +238,7 @@
 #define Throw    ex_throw
 #define Rethrow  ex_rethrow
 #define Shield   ex_shield
+#define Defer    ex_defer
 #elif defined(__EX_NS_CXX__) || (!defined(__cplusplus) && !defined(__EX_NS_CUSTOM__))
 #define try      ex_try
 #define cleanup  ex_cleanup
@@ -225,6 +246,7 @@
 #define throw    ex_throw
 #define rethrow  ex_rethrow
 #define shield   ex_shield
+#define defer    ex_defer
 #endif
 
 #endif /* __EX_H__ */


ossp-pkg/ex/ex.pod 1.20 -> 1.21

--- ex.pod       2002/02/25 10:30:15     1.20
+++ ex.pod       2002/03/07 21:30:10     1.21
@@ -48,10 +48,14 @@
 
 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
@@ -214,6 +218,22 @@
 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 silently ignored and on leaving the I<BLOCK>
+the first occurred exception is thrown.
+
+The B<ex_defer> 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
@@ -235,6 +255,12 @@
 of an B<ex_try> clause to test whether the current scope is exception
 catching. 
 
+=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.
+
 =item B<ex_shielding>
 
 This is a boolean flag which can be checked inside the dynamic scope of
@@ -656,7 +682,10 @@
 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.
+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
 


ossp-pkg/ex/ex_test.c 1.8 -> 1.9

--- ex_test.c    2002/01/30 10:40:07     1.8
+++ ex_test.c    2002/03/07 21:30:10     1.9
@@ -111,6 +111,44 @@
     }
 }
 
+TS_TEST(test_defer)
+{
+    ex_t ex;
+    volatile int i1 = 0;
+    volatile int i2 = 0;
+    volatile int i3 = 0;
+
+    ts_test_check(TS_CTX, "exception deferring");
+    if (ex_deferring)
+        ts_test_fail(TS_CTX, "unexpected deferring scope");
+    ex_try {
+        ex_defer {
+            if (!ex_deferring)
+                ts_test_fail(TS_CTX, "unexpected non-deferring scope");
+            ex_defer {
+                i1 = 1;
+                ex_throw(0, 0, 4711);
+                i2 = 2;
+                ex_throw(0, 0, 0);
+                i3 = 3;
+                ex_throw(0, 0, 0);
+            }
+            ex_throw(0, 0, 0);
+        }
+        ts_test_fail(TS_CTX, "unexpected not occurred deferred throwing");
+    }
+    ex_catch (ex) {
+        if ((long)ex.ex_value != (long)4711)
+            ts_test_fail(TS_CTX, "caught exception with value %d, expected 4711", (long)ex.ex_value);
+    }
+    if (i1 != 1)
+        ts_test_fail(TS_CTX, "v.i1 not set (expected 1, got %d)", i1);
+    if (i2 != 2)
+        ts_test_fail(TS_CTX, "v.i2 not set (expected 2, got %d)", i2);
+    if (i3 != 3)
+        ts_test_fail(TS_CTX, "v.i3 not set (expected 3, got %d)", i3);
+}
+
 TS_TEST(test_shield)
 {
     ex_t ex;
@@ -178,6 +216,7 @@
     ts_suite_test(ts, test_controlflow, "basic nested control flow");
     ts_suite_test(ts, test_value,       "exception value passing");
     ts_suite_test(ts, test_variables,   "variable value preservation");
+    ts_suite_test(ts, test_defer,       "exception deferring");
     ts_suite_test(ts, test_shield,      "exception shielding");
     ts_suite_test(ts, test_cleanup,     "cleanup handling");
     n = ts_suite_run(ts);

CVSTrac 2.0.1