X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;f=ucw%2Ftrans.c;h=db8838372db0d8a2d7f3dd70944a840ce2cfffdc;hb=0db6e10eac28f38bfc3b325b13ad95107c58ce1e;hp=e2e881b8738c3e58b2a73c988a601072d26bc214;hpb=efff2660a416682116f97e0828f1f29a94998b22;p=libucw.git diff --git a/ucw/trans.c b/ucw/trans.c index e2e881b8..db883837 100644 --- a/ucw/trans.c +++ b/ucw/trans.c @@ -1,18 +1,18 @@ /* * 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" -#include "ucw/respool.h" -#include "ucw/mempool.h" +#include +#include +#include +#include #include @@ -37,7 +37,7 @@ trans_cleanup(void) } struct trans * -trans_open_rp(struct respool *rp) +trans_open(void) { trans_init(); struct ucwlib_context *c = ucwlib_thread_context(); @@ -47,11 +47,11 @@ trans_open_rp(struct respool *rp) struct trans *t = mp_alloc(mp, sizeof(*t)); t->trans_pool_state = mst; - if (!rp) - rp = rp_new("trans", mp); + struct respool *rp = rp_new("trans", mp); 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); @@ -64,33 +64,79 @@ trans_get_current(void) return ucwlib_thread_context() -> current_trans; } +struct mempool * +trans_get_pool(void) +{ + return ucwlib_thread_context() -> trans_pool; +} + static void -trans_close(struct trans *t) +trans_pop(struct trans *t, struct ucwlib_context *c) { - struct ucwlib_context *c = ucwlib_thread_context(); + DBG("... popping trans %p", t); rp_switch(t->prev_rpool); c->current_trans = t->prev_trans; +} + +static void +trans_drop(struct trans *t, struct ucwlib_context *c) +{ + DBG("... dropping trans %p", t); mp_restore(c->trans_pool, t->trans_pool_state); } void trans_commit(void) { - struct trans *t = trans_get_current(); - DBG("Commiting transaction %p", t); + struct ucwlib_context *c = ucwlib_thread_context(); + struct trans *t = c->current_trans; + DBG("Committing transaction %p", t); ASSERT(t); - rp_detach(t->rpool); - trans_close(t); + ASSERT(!t->thrown_exc); + rp_commit(t->rpool); + trans_pop(t, c); + trans_drop(t, c); } void trans_rollback(void) { - struct trans *t = trans_get_current(); + struct ucwlib_context *c = ucwlib_thread_context(); + struct trans *t = c->current_trans; DBG("Rolling back transaction %p", t); ASSERT(t); + ASSERT(!t->thrown_exc); rp_delete(t->rpool); - trans_close(t); + trans_pop(t, c); + trans_drop(t, c); +} + +void +trans_fold(void) +{ + struct ucwlib_context *c = ucwlib_thread_context(); + 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 +} + +void +trans_caught(void) +{ + // Exception has been finally caught. Roll back the current transaction, + // including all sub-transactions that have been folded to it during + // propagation. + struct trans *t = trans_get_current(); + struct exception *x = t->thrown_exc; + ASSERT(x); + DBG("... exception %p caught", x); + t->thrown_exc = NULL; + trans_rollback(); } void @@ -102,22 +148,128 @@ trans_dump(void) puts("No transaction open."); return; } + while (t) { printf("Transaction %p:\n", t); - rp_dump(t->rpool); + 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; } } +void +trans_throw_exc(struct exception *x) +{ + struct ucwlib_context *c = ucwlib_thread_context(); + struct trans *t = c->current_trans; + DBG("Throwing exception %s (%s) in trans %p", x->id, x->msg, t); + if (!t) + die("%s", x->msg); + + // If we are already handling an exception (i.e., throw from a catch handler), + // fold the current transaction into its parent. + while (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. + t->thrown_exc = x; + longjmp(t->jmp, 1); +} + +void +trans_throw(const char *id, void *object, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + trans_vthrow(id, object, fmt, args); +} + +void +trans_vthrow(const char *id, void *object, const char *fmt, va_list args) +{ + struct mempool *mp = trans_get_pool(); + if (!mp) + vdie(fmt, args); + struct exception *x = mp_alloc(mp, sizeof(*x)); + x->id = id; + x->object = object; + x->msg = mp_vprintf(mp, fmt, args); + trans_throw_exc(x); +} + +struct exception * +trans_current_exc(void) +{ + return trans_get_current() -> thrown_exc; +} + #ifdef TEST +static void trc_free(struct resource *r) +{ + printf("Freeing object #%d\n", (int)(intptr_t) r->priv); +} + +static struct res_class trc = { + .name = "test", + .free = trc_free, +}; + int main(void) { + TRANS_TRY + { + res_new(&trc, (void *)(intptr_t) 1); + res_malloc(64, NULL); + TRANS_TRY + { + res_new(&trc, (void *)(intptr_t) 2); + res_malloc(128, NULL); + trans_throw("ucw.test", "inn", "Universe failure: %d+%d=%d", 1, 2, 4); + } + TRANS_CATCH(x) + { + printf("Inner catch: %s\n", x->msg); + trans_dump(); + TRANS_TRY + { + res_malloc(256, NULL); + trans_throw("ucw.test.nested", "nest", "Something is wrong recursively"); + } + TRANS_CATCH(y) + { + printf("Yet another layer: %s\n", y->msg); + trans_dump(); + // trans_throw_exc(y); + } + TRANS_END; + 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."); +#if 0 trans_open(); res_malloc(64, NULL); trans_dump(); trans_commit(); +#endif trans_cleanup(); return 0; }