]> mj.ucw.cz Git - libucw.git/blob - ucw/trans.c
strtonum: Added small bits of documentation
[libucw.git] / ucw / trans.c
1 /*
2  *      The UCW Library -- Transactions
3  *
4  *      (c) 2008--2011 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 #undef LOCAL_DEBUG
11
12 #include <ucw/lib.h>
13 #include <ucw/trans.h>
14 #include <ucw/resource.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 }
26
27 void
28 trans_cleanup(void)
29 {
30   struct ucwlib_context *c = ucwlib_thread_context();
31   if (c->trans_pool)
32     {
33       mp_delete(c->trans_pool);
34       c->trans_pool = NULL;
35     }
36   c->current_trans = NULL;
37 }
38
39 struct trans *
40 trans_open(void)
41 {
42   trans_init();
43   struct ucwlib_context *c = ucwlib_thread_context();
44   struct mempool *mp = c->trans_pool;
45
46   struct mempool_state *mst = mp_push(mp);
47   struct trans *t = mp_alloc(mp, sizeof(*t));
48   t->trans_pool_state = mst;
49
50   struct respool *rp = rp_new("trans", mp);
51   t->rpool = rp;
52   t->prev_rpool = rp_switch(rp);
53
54   t->thrown_exc = NULL;
55   t->prev_trans = c->current_trans;
56   c->current_trans = t;
57   DBG("Opened transaction %p", t);
58   return t;
59 }
60
61 struct trans *
62 trans_get_current(void)
63 {
64   return ucwlib_thread_context() -> current_trans;
65 }
66
67 struct mempool *
68 trans_get_pool(void)
69 {
70   return ucwlib_thread_context() -> trans_pool;
71 }
72
73 static void
74 trans_pop(struct trans *t, struct ucwlib_context *c)
75 {
76   DBG("... popping trans %p", t);
77   rp_switch(t->prev_rpool);
78   c->current_trans = t->prev_trans;
79 }
80
81 static void
82 trans_drop(struct trans *t, struct ucwlib_context *c)
83 {
84   DBG("... dropping trans %p", t);
85   mp_restore(c->trans_pool, t->trans_pool_state);
86 }
87
88 void
89 trans_commit(void)
90 {
91   struct ucwlib_context *c = ucwlib_thread_context();
92   struct trans *t = c->current_trans;
93   DBG("Committing transaction %p", t);
94   ASSERT(t);
95   ASSERT(!t->thrown_exc);
96   rp_commit(t->rpool);
97   trans_pop(t, c);
98   trans_drop(t, c);
99 }
100
101 void
102 trans_rollback(void)
103 {
104   struct ucwlib_context *c = ucwlib_thread_context();
105   struct trans *t = c->current_trans;
106   DBG("Rolling back transaction %p", t);
107   ASSERT(t);
108   ASSERT(!t->thrown_exc);
109   rp_delete(t->rpool);
110   trans_pop(t, c);
111   trans_drop(t, c);
112 }
113
114 void
115 trans_fold(void)
116 {
117   struct ucwlib_context *c = ucwlib_thread_context();
118   struct trans *t = c->current_trans;
119   DBG("Folding transaction %p", t);
120   ASSERT(t);
121   ASSERT(!t->thrown_exc);
122   trans_pop(t, c);
123   ASSERT(c->current_trans);     // Ensure that the parent exists
124   res_subpool(t->rpool);
125   t->rpool = NULL;              // To stop people from using the trans
126 }
127
128 void
129 trans_caught(void)
130 {
131   // Exception has been finally caught. Roll back the current transaction,
132   // including all sub-transactions that have been folded to it during
133   // propagation.
134   struct trans *t = trans_get_current();
135   struct exception *x = t->thrown_exc;
136   ASSERT(x);
137   DBG("... exception %p caught", x);
138   t->thrown_exc = NULL;
139   trans_rollback();
140 }
141
142 void
143 trans_dump(void)
144 {
145   struct trans *t = trans_get_current();
146   if (!t)
147     {
148       puts("No transaction open.");
149       return;
150     }
151
152   while (t)
153     {
154       printf("Transaction %p:\n", t);
155       struct exception *x = t->thrown_exc;
156       if (x)
157         printf("    Exception %s (%s) in flight\n", x->id, x->msg);
158       rp_dump(t->rpool, 4);
159       t = t->prev_trans;
160     }
161 }
162
163 void
164 trans_throw_exc(struct exception *x)
165 {
166   struct ucwlib_context *c = ucwlib_thread_context();
167   struct trans *t = c->current_trans;
168   DBG("Throwing exception %s (%s) in trans %p", x->id, x->msg, t);
169   if (!t)
170     die("%s", x->msg);
171
172   // If we are already handling an exception (i.e., throw from a catch handler),
173   // fold the current transaction into its parent.
174   while (t->thrown_exc)
175     {
176       if (!t->prev_trans)
177         die("%s", x->msg);
178       t->thrown_exc = NULL;
179       trans_fold();
180       t = c->current_trans;
181       DBG("... recursive throw propagated to parent trans %p", t);
182     }
183
184   // And jump overboard.
185   t->thrown_exc = x;
186   longjmp(t->jmp, 1);
187 }
188
189 void
190 trans_throw(const char *id, void *object, const char *fmt, ...)
191 {
192   va_list args;
193   va_start(args, fmt);
194   trans_vthrow(id, object, fmt, args);
195 }
196
197 void
198 trans_vthrow(const char *id, void *object, const char *fmt, va_list args)
199 {
200   struct mempool *mp = trans_get_pool();
201   if (!mp)
202     vdie(fmt, args);
203   struct exception *x = mp_alloc(mp, sizeof(*x));
204   x->id = id;
205   x->object = object;
206   x->msg = mp_vprintf(mp, fmt, args);
207   trans_throw_exc(x);
208 }
209
210 struct exception *
211 trans_current_exc(void)
212 {
213   return trans_get_current() -> thrown_exc;
214 }
215
216 #ifdef TEST
217
218 static void trc_free(struct resource *r)
219 {
220   printf("Freeing object #%d\n", (int)(intptr_t) r->priv);
221 }
222
223 static struct res_class trc = {
224   .name = "test",
225   .free = trc_free,
226 };
227
228 int main(void)
229 {
230   TRANS_TRY
231     {
232       res_new(&trc, (void *)(intptr_t) 1);
233       res_malloc(64, NULL);
234       TRANS_TRY
235         {
236           res_new(&trc, (void *)(intptr_t) 2);
237           res_malloc(128, NULL);
238           trans_throw("ucw.test", "inn", "Universe failure: %d+%d=%d", 1, 2, 4);
239         }
240       TRANS_CATCH(x)
241         {
242           printf("Inner catch: %s\n", x->msg);
243           trans_dump();
244           TRANS_TRY
245             {
246               res_malloc(256, NULL);
247               trans_throw("ucw.test.nested", "nest", "Something is wrong recursively");
248             }
249           TRANS_CATCH(y)
250             {
251               printf("Yet another layer: %s\n", y->msg);
252               trans_dump();
253               // trans_throw_exc(y);
254             }
255           TRANS_END;
256           trans_throw("ucw.test2", "out", "Error: %s", x->msg);
257         }
258       TRANS_END;
259     }
260   TRANS_CATCH(x)
261     {
262       printf("Outer catch: %s\n", x->msg);
263       trans_dump();
264     }
265   TRANS_END;
266   trans_throw("ucw.test3", "glob", "Global error. Reboot globe.");
267 #if 0
268   trans_open();
269   res_malloc(64, NULL);
270   trans_dump();
271   trans_commit();
272 #endif
273   trans_cleanup();
274   return 0;
275 }
276
277 #endif