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