]> mj.ucw.cz Git - libucw.git/blobdiff - ucw/trans.c
Build: Just removed redundant parentheses.
[libucw.git] / ucw / trans.c
index 1ceb3948423d8b836e484b5b44b45af6528a833f..db8838372db0d8a2d7f3dd70944a840ce2cfffdc 100644 (file)
 /*
  *     The UCW Library -- Transactions
  *
- *     (c) 2008 Martin Mares <mj@ucw.cz>
+ *     (c) 2008--2011 Martin Mares <mj@ucw.cz>
  *
  *     This software may be freely distributed and used according to the terms
  *     of the GNU Lesser General Public License.
  */
 
-#include "ucw/lib.h"
-#include "ucw/trans.h"
-#include "ucw/respool.h"
-#include "ucw/mempool.h"
+#undef LOCAL_DEBUG
 
+#include <ucw/lib.h>
+#include <ucw/trans.h>
+#include <ucw/resource.h>
+#include <ucw/mempool.h>
+
+#include <stdio.h>
+
+void
+trans_init(void)
+{
+  struct ucwlib_context *c = ucwlib_thread_context();
+  if (!c->trans_pool)
+    c->trans_pool = mp_new(1024);
+}
+
+void
+trans_cleanup(void)
+{
+  struct ucwlib_context *c = ucwlib_thread_context();
+  if (c->trans_pool)
+    {
+      mp_delete(c->trans_pool);
+      c->trans_pool = NULL;
+    }
+  c->current_trans = NULL;
+}
+
+struct trans *
+trans_open(void)
+{
+  trans_init();
+  struct ucwlib_context *c = ucwlib_thread_context();
+  struct mempool *mp = c->trans_pool;
+
+  struct mempool_state *mst = mp_push(mp);
+  struct trans *t = mp_alloc(mp, sizeof(*t));
+  t->trans_pool_state = mst;
+
+  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);
+  return t;
+}
+
+struct trans *
+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_pop(struct trans *t, struct ucwlib_context *c)
+{
+  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 ucwlib_context *c = ucwlib_thread_context();
+  struct trans *t = c->current_trans;
+  DBG("Committing transaction %p", t);
+  ASSERT(t);
+  ASSERT(!t->thrown_exc);
+  rp_commit(t->rpool);
+  trans_pop(t, c);
+  trans_drop(t, c);
+}
+
+void
+trans_rollback(void)
+{
+  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_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
+trans_dump(void)
+{
+  struct trans *t = trans_get_current();
+  if (!t)
+    {
+      puts("No transaction open.");
+      return;
+    }
+
+  while (t)
+    {
+      printf("Transaction %p:\n", t);
+      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;
+}
+
+#endif