From: Martin Mares Date: Wed, 3 Sep 2008 11:27:10 +0000 (+0200) Subject: Implemented exceptions. X-Git-Tag: v5.0~74^2~37 X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=f0da660715a0061bc350e159f3689755c79ab006;p=libucw.git Implemented exceptions. --- diff --git a/ucw/respool.h b/ucw/respool.h index 0c3449cd..c010b4a3 100644 --- a/ucw/respool.h +++ b/ucw/respool.h @@ -32,6 +32,7 @@ struct resource { struct respool *rpool; const struct res_class *rclass; void *priv; // Private to the class + // More data specific for the particular class can follow }; struct res_class { diff --git a/ucw/threads.h b/ucw/threads.h index e0ff0150..57b5a774 100644 --- a/ucw/threads.h +++ b/ucw/threads.h @@ -22,6 +22,8 @@ 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 e2e881b8..bbe7ca7c 100644 --- a/ucw/trans.c +++ b/ucw/trans.c @@ -22,6 +22,8 @@ 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 @@ -31,7 +33,9 @@ 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; } @@ -64,39 +68,103 @@ trans_get_current(void) return ucwlib_thread_context() -> current_trans; } +struct mempool * +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_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); + ASSERT(!c->current_exc); rp_detach(t->rpool); - trans_close(t); + 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 trans *t = trans_get_current(); - DBG("Rolling back transaction %p", t); + 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); rp_delete(t->rpool); - trans_close(t); + trans_pop(t, c); + trans_drop(t, c); } void trans_dump(void) { - struct trans *t = trans_get_current(); + struct ucwlib_context *c = ucwlib_thread_context(); + struct exception *x = c->current_exc; + struct trans *t = c->current_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); + tx = tx->prev_trans; + } + } + if (!t) { puts("No transaction open."); @@ -110,14 +178,94 @@ trans_dump(void) } } +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); + + // 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); + + // And jump overboard. + c->current_exc = x; + longjmp(t->jmp, 1); +} + +void +trans_throw(const char *id, void *object, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + + struct mempool *mp = trans_get_pool(); + 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 ucwlib_thread_context() -> current_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_throw("ucw.test2", "out", "Error: %s", x->msg); + } + TRANS_END; + } + TRANS_CATCH(x) + { + printf("Outer catch: %s\n", x->msg); + } + 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; } diff --git a/ucw/trans.h b/ucw/trans.h index 1b9e799b..f352269c 100644 --- a/ucw/trans.h +++ b/ucw/trans.h @@ -37,6 +37,39 @@ void trans_commit(void); void trans_rollback(void); void trans_dump(void); +struct mempool *trans_get_pool(void); +struct mempool *trans_get_exc_pool(void); + /* Exceptions */ +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; + +struct exception *trans_current_exc(void); + +#define TRANS_TRY do { \ + struct trans *_t = trans_open(); \ + if (!setjmp(_t->jmp)) \ + { + +#define TRANS_CATCH(x) \ + trans_commit(); \ + } \ + else \ + { \ + struct exception *x UNUSED = trans_current_exc(); + +#define TRANS_END \ + trans_rollback(); \ + } \ + } while(0) + #endif