]> mj.ucw.cz Git - libucw.git/blob - ucw/trans.c
d91437b46cec344b25cc637a30d1cc8237b7e43b
[libucw.git] / ucw / trans.c
1 /*
2  *      The UCW Library -- Transactions
3  *
4  *      (c) 2008 Martin Mares <mj@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU Lesser General Public License.
8  */
9
10 #define LOCAL_DEBUG
11
12 #include "ucw/lib.h"
13 #include "ucw/trans.h"
14 #include "ucw/respool.h"
15 #include "ucw/mempool.h"
16
17 #include <stdio.h>
18
19 void
20 trans_init(void)
21 {
22   struct ucwlib_context *c = ucwlib_thread_context();
23   if (!c->trans_pool)
24     c->trans_pool = mp_new(1024);
25   if (!c->exc_pool)
26     c->exc_pool = mp_new(1024);
27 }
28
29 void
30 trans_cleanup(void)
31 {
32   struct ucwlib_context *c = ucwlib_thread_context();
33   if (c->trans_pool)
34     {
35       mp_delete(c->trans_pool);
36       mp_delete(c->exc_pool);
37       c->trans_pool = NULL;
38       c->exc_pool = NULL;
39     }
40   c->current_trans = NULL;
41 }
42
43 struct trans *
44 trans_open(void)
45 {
46   trans_init();
47   struct ucwlib_context *c = ucwlib_thread_context();
48   struct mempool *mp = c->trans_pool;
49
50   struct mempool_state *mst = mp_push(mp);
51   struct trans *t = mp_alloc(mp, sizeof(*t));
52   t->trans_pool_state = mst;
53
54   struct respool *rp = rp_new("trans", mp);
55   t->rpool = rp;
56   t->prev_rpool = rp_switch(rp);
57
58   t->prev_trans = c->current_trans;
59   c->current_trans = t;
60   DBG("Opened transaction %p", t);
61   return t;
62 }
63
64 struct trans *
65 trans_get_current(void)
66 {
67   return ucwlib_thread_context() -> current_trans;
68 }
69
70 struct mempool *
71 trans_get_pool(void)
72 {
73   return ucwlib_thread_context() -> trans_pool;
74 }
75
76 struct mempool *
77 trans_get_exc_pool(void)
78 {
79   return ucwlib_thread_context() -> exc_pool;
80 }
81
82 static void
83 trans_pop(struct trans *t, struct ucwlib_context *c)
84 {
85   DBG("... popping trans %p", t);
86   rp_switch(t->prev_rpool);
87   c->current_trans = t->prev_trans;
88 }
89
90 static void
91 trans_drop(struct trans *t, struct ucwlib_context *c)
92 {
93   DBG("... dropping trans %p", t);
94   mp_restore(c->trans_pool, t->trans_pool_state);
95 }
96
97 void
98 trans_commit(void)
99 {
100   struct ucwlib_context *c = ucwlib_thread_context();
101   struct trans *t = c->current_trans;
102   DBG("Committing transaction %p", t);
103   ASSERT(t);
104   ASSERT(!c->current_exc);
105   rp_detach(t->rpool);
106   trans_pop(t, c);
107   trans_drop(t, c);
108 }
109
110 static void
111 trans_rollback_exc(struct ucwlib_context *c)
112 {
113   // In case we were processing an exception, roll back all transactions
114   // through which the exception has propagated.
115   struct exception *x = c->current_exc;
116   struct trans *t = x->trans;
117   while (t != c->current_trans)
118     {
119       DBG("Rolling back transaction %p after exception", t);
120       struct trans *tprev = t->prev_trans;
121       rp_delete(t->rpool);
122       trans_drop(t, c);
123       t = tprev;
124     }
125
126   c->current_exc = NULL;
127   mp_flush(c->exc_pool);
128 }
129
130 void
131 trans_rollback(void)
132 {
133   struct ucwlib_context *c = ucwlib_thread_context();
134   if (c->current_exc)
135     {
136       trans_rollback_exc(c);
137       return;
138     }
139
140   struct trans *t = c->current_trans;
141   ASSERT(t);
142   DBG("Rolling back transaction %p", t);
143   rp_delete(t->rpool);
144   trans_pop(t, c);
145   trans_drop(t, c);
146 }
147
148 void
149 trans_dump(void)
150 {
151   struct ucwlib_context *c = ucwlib_thread_context();
152   struct exception *x = c->current_exc;
153   struct trans *t = c->current_trans;
154
155   if (x)
156     {
157       printf("Exception %s (%s) in flight\n", x->id, x->msg);
158       struct trans *tx = x->trans;
159       while (tx != t)
160         {
161           printf("Recovering transaction %p:\n", tx);
162           rp_dump(tx->rpool, 0);
163           tx = tx->prev_trans;
164         }
165     }
166
167   if (!t)
168     {
169       puts("No transaction open.");
170       return;
171     }
172   while (t)
173     {
174       printf("Transaction %p:\n", t);
175       rp_dump(t->rpool, 0);
176       t = t->prev_trans;
177     }
178 }
179
180 void
181 trans_throw_exc(struct exception *x)
182 {
183   struct ucwlib_context *c = ucwlib_thread_context();
184   struct trans *t = c->current_trans;
185   DBG("Throwing exception %s (%s) in trans %p", x->id, x->msg, t);
186   if (!t)
187     die("%s", x->msg);
188
189   // Remember which transaction have the exceptions started to propagate from
190   if (c->current_exc)
191     x->trans = c->current_exc->trans;
192   else
193     x->trans = t;
194
195   // Pop the current transaction off the transaction stack, but do not roll it
196   // back, it will be done when the exception stops propagating.
197   trans_pop(t, c);
198
199   // And jump overboard.
200   c->current_exc = x;
201   longjmp(t->jmp, 1);
202 }
203
204 void
205 trans_throw(const char *id, void *object, const char *fmt, ...)
206 {
207   va_list args;
208   va_start(args, fmt);
209   trans_vthrow(id, object, fmt, args);
210 }
211
212 void
213 trans_vthrow(const char *id, void *object, const char *fmt, va_list args)
214 {
215   struct mempool *mp = trans_get_pool();
216   if (!mp)
217     vdie(fmt, args);
218   struct exception *x = mp_alloc(mp, sizeof(*x));
219   x->id = id;
220   x->object = object;
221   x->msg = mp_vprintf(mp, fmt, args);
222   trans_throw_exc(x);
223 }
224
225 struct exception *
226 trans_current_exc(void)
227 {
228   return ucwlib_thread_context() -> current_exc;
229 }
230
231 #ifdef TEST
232
233 static void trc_free(struct resource *r)
234 {
235   printf("Freeing object #%d\n", (int)(intptr_t) r->priv);
236 }
237
238 static struct res_class trc = {
239   .name = "test",
240   .free = trc_free,
241 };
242
243 int main(void)
244 {
245   TRANS_TRY
246     {
247       res_new(&trc, (void *)(intptr_t) 1);
248       res_malloc(64, NULL);
249       TRANS_TRY
250         {
251           res_new(&trc, (void *)(intptr_t) 2);
252           res_malloc(128, NULL);
253           trans_throw("ucw.test", "inn", "Universe failure: %d+%d=%d", 1, 2, 4);
254         }
255       TRANS_CATCH(x)
256         {
257           printf("Inner catch: %s\n", x->msg);
258           trans_dump();
259           //trans_throw("ucw.test2", "out", "Error: %s", x->msg);
260         }
261       TRANS_END;
262     }
263   TRANS_CATCH(x)
264     {
265       printf("Outer catch: %s\n", x->msg);
266     }
267   TRANS_END;
268   trans_throw("ucw.test3", "glob", "Global error. Reboot globe.");
269 #if 0
270   trans_open();
271   res_malloc(64, NULL);
272   trans_dump();
273   trans_commit();
274 #endif
275   trans_cleanup();
276   return 0;
277 }
278
279 #endif