]> mj.ucw.cz Git - libucw.git/blob - ucw/doc/trans.txt
Resource pools documented
[libucw.git] / ucw / doc / trans.txt
1 Transactions and resource tracking
2 ==================================
3
4 LibUCW is equipped with a general system for keeping track of resources
5 (allocated memory, open files, ...) and freeing them when requested to.
6
7 The resource tracker can be used either separately (in the form of explicitly
8 managed resource pools) or within a transactional layer, which offers
9 exceptions similar to those in higher-level languages. An exception
10 then rolls back the transaction, freeing all temporary resources allocated
11 within the transaction.
12
13 Resource pools: ucw/respool.h
14 -----------------------------
15
16 A resource pool contains a stack of resources. When a new resource
17 is created, it is pushed onto the stack. When freeing the pool, the
18 resources are freed in the opposite order, which allows a resource
19 refer to data of previously created resources.
20
21 A resource can be also freed separately (which unlinks it from the pool),
22 or *detached* from the pool (which keeps the real resource, but forgets
23 its meta-data, so the resource is no longer tracked).
24
25 For each thread, LibUCW remembers the currently active resource pool.
26 One pool can be used for at most one thread at a time. All functions
27 which create resources do so in the active pool. All other functions
28 operating on resources work on both active and in-active pools.
29
30 !!ucw/respool.h
31
32 Transactions: ucw/trans.h
33 -------------------------
34
35 Assorted notes:
36
37 - A transaction is tied to a thread which has created it. A transaction
38   can create a sub-transaction, so every thread keeps a stack of running
39   transactions in its per-thread data. Calling @trans_init() is optional,
40   but @trans_cleanup() should be used before a thread exits in order to
41   free resources used by transaction system.
42
43 - Every transaction keeps a resource pool containing resources, which
44   have been created during the transaction. Whenever the transaction is
45   running, this pool is set as current. You are allowed to switch to
46   a different pool, but please do so carefully.
47 +
48   When a transaction ends, the pool is destroyed and the previous active
49   pool is popped off the transaction stack. The fate of the resources
50   inside the pool depends on the operation used to end the transaction:
51         * commit -- all resources are detached from the pool
52         * rollback -- all resources are freed
53         * fold -- instead of destroying the pool, it is added as a subpool
54           to the parent transaction (which must exist)
55
56 - Each transaction also includes a memory pool, from which all temporary
57   structures (including all resources created by the transaction) are
58   allocated. Feel free to allocate your temporary data from this pool, too;
59   they will be freed when the transaction is committed or rolled back.
60   When the transaction ends with a fold, this pool gets included inside
61   the parent transaction's pool.
62 +
63   (To be true, there is actually a shared transaction pool per thread
64   and the transaction logic uses @mp_push() and @mp_pop() to keep a stack
65   of per-transaction data.)
66
67 - Transactions are usually used together with exceptions (which are similar
68   to how exceptions work in other languages, but they differ in subtle details,
69   so please read carefully). When a failure condition of some kind is detected,
70   an exception is raised. It involves creating an exception object and jumping
71   out of the transaction by a `longjmp()`. The exception object (`struct exception`)
72   contains an identification of the error and possibly additional data.
73 +
74   Usually, creation of an transaction and handling of exceptions is done
75   using helper macros (it is not strictly necessary, but highly recommended):
76 +
77         TRANS_TRY
78           {
79             // Code that runs inside the transaction.
80           }
81         TRANS_CATCH(x)
82           {
83             // When an exception is raised, execution continues here.
84           }
85         TRANS_END;
86
87   The code inside the transaction ends with an implicit @trans_commit().
88   If you want to end the transaction in a different way, you can do so,
89   but you need to use a `break` statement to skip the implicit commit.
90
91   The exception handling code gets a local variable `x` pointing to the
92   exception object. When the exception is handled (for example, an error
93   message is logged), @trans_caught() is called automatically, which rolls
94   back the transaction and frees all its resources. Again, you can use the
95   `break` statement to skip this.
96
97   Alternatively, when you are in a nested transaction, you can throw a different
98   exception or re-throw the original one. This raises an exception in the
99   context of the parent transaction. In this case, the child transaction is
100   not rolled back, but its pools are folded as sub-pools of the parent transaction
101   and kept until @trans_caught() is called finally.
102
103 * When an exception is thrown outside a transaction, it is converted to
104   a plain @die().
105
106 * Memory management and lifetime of various objects and pools deserve special
107   attention, as usually when non-local jumps are taking place. When an exception
108   is raised, the exception structure is allocated from the memory pool of the
109   current transaction. When the exception is propagated through the stack of
110   transactions, no transaction is ever rolled back -- all of them are folded
111   and their pools remain accessible until @trans_caught() is called at the end.
112   Therefore exceptions can carry pointers to the objects which have failed
113   without a risk of the object becoming invalid. However, you need to avoid
114   pointing to on-stack data like local variables of functions, because these
115   are of course destroyed during the `longjmp()`.
116
117 FIXME: Interaction between exceptions, pools and other libucw modules.
118
119 FIXME: Unit tests
120
121 FIXME: Resourcification of more libucw objects
122
123 FIXME: Do we want to allow res_alloc() when no pool is active.