]> mj.ucw.cz Git - libucw.git/commitdiff
Implemented exceptions.
authorMartin Mares <mj@ucw.cz>
Wed, 3 Sep 2008 11:27:10 +0000 (13:27 +0200)
committerMartin Mares <mj@ucw.cz>
Tue, 29 Mar 2011 10:55:05 +0000 (12:55 +0200)
ucw/respool.h
ucw/threads.h
ucw/trans.c
ucw/trans.h

index 0c3449cd2c3f9ad8894aca17f6ce872c7ef067c8..c010b4a3f43b93c603732990f2ac4b5fbbccd440 100644 (file)
@@ -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 {
index e0ff01505dba2b878b373f20a6101b22c1fa7cf1..57b5a7743941c6241c4e745faabe60ceb4b86779 100644 (file)
@@ -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
index e2e881b8738c3e58b2a73c988a601072d26bc214..bbe7ca7cef7ee7d569aa216224ca1728f8df3491 100644 (file)
@@ -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;
 }
index 1b9e799babd3bc05738b3337b214e742c2906d47..f352269c2cac2d595ec1e7f0206f9727efeb50e2 100644 (file)
@@ -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