From: Martin Mares Date: Sun, 17 Apr 2011 17:06:54 +0000 (+0200) Subject: Trans: New semantics of exceptions X-Git-Tag: v5.0~74^2~26 X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=349d89dca4a4ab2420b57c8a0f1d233f3af4cd1c;p=libucw.git Trans: New semantics of exceptions Changed exception propagation rules according to the new semantics documented in ucw/doc/trans.txt, which is based on transaction folding. We avoid the (partially implemented) exception pool altogether, all exceptions are just allocated from the pool of the current transaction, and clever re-throwing rules make the pools last as long as they should. Also, the current exception is kept track of in the current transaction instead of the thread context. --- diff --git a/ucw/threads.h b/ucw/threads.h index 57b5a774..1d16e55f 100644 --- a/ucw/threads.h +++ b/ucw/threads.h @@ -1,7 +1,7 @@ /* * The UCW Library -- Threading Helpers * - * (c) 2006--2010 Martin Mares + * (c) 2006--2011 Martin Mares * * This software may be freely distributed and used according to the terms * of the GNU Lesser General Public License. @@ -22,8 +22,6 @@ struct ucwlib_context { struct respool *current_respool; // Current resource pool struct mempool *trans_pool; // Transaction mempool struct trans *current_trans; // Currently open transaction - struct mempool *exc_pool; // Exception mempool - struct exception *current_exc; // Currently active exception }; #ifdef CONFIG_UCW_THREADS diff --git a/ucw/trans.c b/ucw/trans.c index d91437b4..a8de27cd 100644 --- a/ucw/trans.c +++ b/ucw/trans.c @@ -1,13 +1,13 @@ /* * The UCW Library -- Transactions * - * (c) 2008 Martin Mares + * (c) 2008--2011 Martin Mares * * This software may be freely distributed and used according to the terms * of the GNU Lesser General Public License. */ -#define LOCAL_DEBUG +#undef LOCAL_DEBUG #include "ucw/lib.h" #include "ucw/trans.h" @@ -22,8 +22,6 @@ trans_init(void) struct ucwlib_context *c = ucwlib_thread_context(); if (!c->trans_pool) c->trans_pool = mp_new(1024); - if (!c->exc_pool) - c->exc_pool = mp_new(1024); } void @@ -33,9 +31,7 @@ trans_cleanup(void) if (c->trans_pool) { mp_delete(c->trans_pool); - mp_delete(c->exc_pool); c->trans_pool = NULL; - c->exc_pool = NULL; } c->current_trans = NULL; } @@ -55,6 +51,7 @@ trans_open(void) t->rpool = rp; t->prev_rpool = rp_switch(rp); + t->thrown_exc = NULL; t->prev_trans = c->current_trans; c->current_trans = t; DBG("Opened transaction %p", t); @@ -73,12 +70,6 @@ trans_get_pool(void) return ucwlib_thread_context() -> trans_pool; } -struct mempool * -trans_get_exc_pool(void) -{ - return ucwlib_thread_context() -> exc_pool; -} - static void trans_pop(struct trans *t, struct ucwlib_context *c) { @@ -101,78 +92,69 @@ trans_commit(void) struct trans *t = c->current_trans; DBG("Committing transaction %p", t); ASSERT(t); - ASSERT(!c->current_exc); + ASSERT(!t->thrown_exc); rp_detach(t->rpool); trans_pop(t, c); trans_drop(t, c); } -static void -trans_rollback_exc(struct ucwlib_context *c) -{ - // In case we were processing an exception, roll back all transactions - // through which the exception has propagated. - struct exception *x = c->current_exc; - struct trans *t = x->trans; - while (t != c->current_trans) - { - DBG("Rolling back transaction %p after exception", t); - struct trans *tprev = t->prev_trans; - rp_delete(t->rpool); - trans_drop(t, c); - t = tprev; - } - - c->current_exc = NULL; - mp_flush(c->exc_pool); -} - void trans_rollback(void) { struct ucwlib_context *c = ucwlib_thread_context(); - if (c->current_exc) - { - trans_rollback_exc(c); - return; - } - struct trans *t = c->current_trans; - ASSERT(t); DBG("Rolling back transaction %p", t); + ASSERT(t); + ASSERT(!t->thrown_exc); rp_delete(t->rpool); trans_pop(t, c); trans_drop(t, c); } void -trans_dump(void) +trans_fold(void) { struct ucwlib_context *c = ucwlib_thread_context(); - struct exception *x = c->current_exc; struct trans *t = c->current_trans; + DBG("Folding transaction %p", t); + ASSERT(t); + ASSERT(!t->thrown_exc); + trans_pop(t, c); + ASSERT(c->current_trans); // Ensure that the parent exists + res_subpool(t->rpool); + t->rpool = NULL; // To stop people from using the trans +} - if (x) - { - printf("Exception %s (%s) in flight\n", x->id, x->msg); - struct trans *tx = x->trans; - while (tx != t) - { - printf("Recovering transaction %p:\n", tx); - rp_dump(tx->rpool, 0); - tx = tx->prev_trans; - } - } +void +trans_caught(struct exception *x) +{ + // Exception has been finally caught. Roll back the current transaction, + // including all sub-transactions that have been folded to it during + // propagation. + DBG("... exception %p caught", x); + struct trans *t = trans_get_current(); + ASSERT(t->thrown_exc == x); + t->thrown_exc = NULL; + trans_rollback(); +} +void +trans_dump(void) +{ + struct trans *t = trans_get_current(); if (!t) { puts("No transaction open."); return; } + while (t) { printf("Transaction %p:\n", t); - rp_dump(t->rpool, 0); + struct exception *x = t->thrown_exc; + if (x) + printf(" Exception %s (%s) in flight\n", x->id, x->msg); + rp_dump(t->rpool, 4); t = t->prev_trans; } } @@ -186,18 +168,20 @@ trans_throw_exc(struct exception *x) if (!t) die("%s", x->msg); - // Remember which transaction have the exceptions started to propagate from - if (c->current_exc) - x->trans = c->current_exc->trans; - else - x->trans = t; - - // Pop the current transaction off the transaction stack, but do not roll it - // back, it will be done when the exception stops propagating. - trans_pop(t, c); + // If we are already handling an exception (i.e., throw from a catch handler), + // fold the current transaction into its parent. + if (t->thrown_exc) + { + if (!t->prev_trans) + die("%s", x->msg); + t->thrown_exc = NULL; + trans_fold(); + t = c->current_trans; + DBG("... recursive throw propagated to parent trans %p", t); + } // And jump overboard. - c->current_exc = x; + t->thrown_exc = x; longjmp(t->jmp, 1); } @@ -225,7 +209,7 @@ trans_vthrow(const char *id, void *object, const char *fmt, va_list args) struct exception * trans_current_exc(void) { - return ucwlib_thread_context() -> current_exc; + return trans_get_current() -> thrown_exc; } #ifdef TEST @@ -256,13 +240,14 @@ int main(void) { printf("Inner catch: %s\n", x->msg); trans_dump(); - //trans_throw("ucw.test2", "out", "Error: %s", x->msg); + trans_throw("ucw.test2", "out", "Error: %s", x->msg); } TRANS_END; } TRANS_CATCH(x) { printf("Outer catch: %s\n", x->msg); + trans_dump(); } TRANS_END; trans_throw("ucw.test3", "glob", "Global error. Reboot globe."); diff --git a/ucw/trans.h b/ucw/trans.h index 3b7220f7..85b42c2a 100644 --- a/ucw/trans.h +++ b/ucw/trans.h @@ -1,7 +1,7 @@ /* * The UCW Library -- Transactions * - * (c) 2008 Martin Mares + * (c) 2008--2011 Martin Mares * * This software may be freely distributed and used according to the terms * of the GNU Lesser General Public License. @@ -21,6 +21,7 @@ struct trans { struct mempool_state *trans_pool_state; struct respool *rpool; struct respool *prev_rpool; + struct exception *thrown_exc; jmp_buf jmp; }; @@ -31,10 +32,10 @@ struct trans *trans_open(void); struct trans *trans_get_current(void); void trans_commit(void); void trans_rollback(void); +void trans_fold(void); void trans_dump(void); struct mempool *trans_get_pool(void); -struct mempool *trans_get_exc_pool(void); /* Exceptions */ @@ -42,13 +43,13 @@ struct exception { const char *id; // Hierarchic identifier of the exception const char *msg; // Error message to present to the user void *object; // Object on which the exception happened - struct trans *trans; // Transaction in which it happened (set by trans_throw*) // More data specific for the particular `id' can follow }; void trans_throw_exc(struct exception *x) NONRET; void trans_throw(const char *id, void *object, const char *fmt, ...) FORMAT_CHECK(printf,3,4) NONRET; void trans_vthrow(const char *id, void *object, const char *fmt, va_list args) NONRET; +void trans_caught(struct exception *x); struct exception *trans_current_exc(void); @@ -65,7 +66,7 @@ struct exception *trans_current_exc(void); struct exception *x UNUSED = trans_current_exc(); #define TRANS_END \ - trans_rollback(); \ + trans_caught(x); \ } \ } while(0)