then rolls back the transaction, freeing all temporary resources allocated
within the transaction.
-Resource pools: ucw/respool.h
------------------------------
+- <<respools,Resource pools>>
+- <<trans,Transactions>>
+- <<exc,Exceptions>>
+- <<excnames,Exception names>>
+
+Resource pools: ucw/respool.h [[respools]]
+------------------------------------------
A resource pool contains a stack of resources. When a new resource
is created, it is pushed onto the stack. When freeing the pool, the
or *detached* from the pool (which keeps the real resource, but forgets
its meta-data, so the resource is no longer tracked).
+In many cases, a combination of both methods is needed: some resources
+are marked as temporary, while some others are permanent. When the
+an operation is completed successfully (and @rp_commit() is called),
+all temporary resources are freed and the permanent ones detached.
+When the operation fails, @rp_delete() deletes all resources. By default,
+all resources are created as temporary. You can make a resource permanent
+by calling @res_permanent(), or change the default in `resource->default_res_flags`.
+
For each thread, LibUCW remembers the currently active resource pool.
One pool can be used for at most one thread at a time. All functions
which create resources do so in the active pool. All other functions
!!ucw/respool.h
-Transactions: ucw/trans.h
--------------------------
-
-Assorted notes:
-
-- A transaction is tied to a thread which has created it. A transaction
- can create a sub-transaction, so every thread keeps a stack of running
- transactions in its per-thread data. Calling @trans_init() is optional,
- but @trans_cleanup() should be used before a thread exits in order to
- free resources used by transaction system.
-
-- Every transaction keeps a resource pool containing resources, which
- have been created during the transaction. Whenever the transaction is
- running, this pool is set as current. You are allowed to switch to
- a different pool, but please do so carefully.
-+
- When a transaction ends, the pool is destroyed and the previous active
- pool is popped off the transaction stack. The fate of the resources
- inside the pool depends on the operation used to end the transaction:
- * commit -- all resources are detached from the pool
- * rollback -- all resources are freed
- * fold -- instead of destroying the pool, it is added as a subpool
- to the parent transaction (which must exist)
-
-- Each transaction also includes a memory pool, from which all temporary
- structures (including all resources created by the transaction) are
- allocated. Feel free to allocate your temporary data from this pool, too;
- they will be freed when the transaction is committed or rolled back.
- When the transaction ends with a fold, this pool gets included inside
- the parent transaction's pool.
-+
- (To be true, there is actually a shared transaction pool per thread
- and the transaction logic uses @mp_push() and @mp_pop() to keep a stack
- of per-transaction data.)
-
-- Transactions are usually used together with exceptions (which are similar
- to how exceptions work in other languages, but they differ in subtle details,
- so please read carefully). When a failure condition of some kind is detected,
- an exception is raised. It involves creating an exception object and jumping
- out of the transaction by a `longjmp()`. The exception object (`struct exception`)
- contains an identification of the error and possibly additional data.
-+
- Usually, creation of an transaction and handling of exceptions is done
- using helper macros (it is not strictly necessary, but highly recommended):
-+
+Transactions: ucw/trans.h [[trans]]
+-----------------------------------
+
+Upon the resource pools, a transactional mechanism is built. A transaction
+consists of a piece of code and a resource pool for temporary objects created
+by the code. Whenever the transaction is running, this pool is set as current.
+You are allowed to switch to a different pool, but please do so carefully.
+
+When a transaction ends, the pool is destroyed and the previous active
+pool is popped off the transaction stack. The fate of the resources
+inside the pool depends on the operation used to end the transaction:
+
+* *commit* -- permanent resources are detached from the pool, temporary
+ resources are freed
+* *rollback* -- all resources are freed
+* *fold* -- instead of destroying the pool, it is added as a subpool
+ to the parent transaction (which must exist)
+
+A transaction is tied to a thread which has created it. A transaction
+can create a sub-transaction, so every thread keeps a stack of running
+transactions in its per-thread data. Calling @trans_init() is optional,
+but @trans_cleanup() should be used before a thread exits in order to
+free resources used by transaction system.
+
+Each transaction also includes a memory pool, from which all temporary
+structures (including all resources created by the transaction) are
+allocated. Feel free to allocate your temporary data from this pool, too;
+they will be freed when the transaction is committed or rolled back.
+When the transaction ends with a fold, this pool gets included inside
+the parent transaction's pool.
+
+(More precisely, there is actually a shared transaction pool per thread
+and the transaction logic uses @mp_push() and @mp_pop() to keep a stack
+of per-transaction data.)
+
+=== Exceptions [[exc]] ===
+
+Transactions are commonly used together with exceptions (which are similar
+to how exceptions work in other languages, but they differ in subtle details,
+so please read carefully). When a failure condition of some kind is detected,
+an exception is *raised* ("*thrown*" is also sometimes used). It involves
+creating an exception object and jumping out of the transaction by
+a `longjmp()`. The exception object (`struct exception`) contains an
+identification of the error and possibly additional data.
+
+Usually, creation of an transaction and handling of exceptions is done
+using *helper macros* (it is not strictly necessary, but highly recommended):
+
TRANS_TRY
{
// Code that runs inside the transaction.
}
TRANS_END;
- The code inside the transaction ends with an implicit @trans_commit().
- If you want to end the transaction in a different way, you can do so,
- but you need to use a `break` statement to skip the implicit commit.
+The code inside the transaction ends with an implicit @trans_commit().
+If you want to end the transaction in a different way, you can do so,
+but you need to use a `break` statement to skip the implicit commit.
+
+The exception handling code gets a local variable `x` pointing to the
+exception object. When the exception is handled (for example, an error
+message is logged), @trans_caught() is called automatically, which rolls
+back the transaction and frees all its resources. Again, you can use the
+`break` statement to skip this.
+
+Alternatively, when you are in a *nested transaction*, you can throw a different
+exception or re-throw the original one. This raises an exception in the
+context of the parent transaction. In this case, the child transaction is
+not rolled back, but its pools are folded as sub-pools of the parent transaction
+and kept until @trans_caught() is called finally.
+
+When an exception is thrown *outside a transaction*, it is converted to
+a plain @die().
+
+*Memory management* and lifetime of various objects and pools deserve special
+attention, as usually when non-local jumps are taking place. When an exception
+is raised, the exception structure is allocated from the memory pool of the
+current transaction. When the exception is propagated through the stack of
+transactions, no transaction is ever rolled back -- all of them are folded
+and their pools remain accessible until @trans_caught() is called at the end.
+Therefore exceptions can carry pointers to the objects which have failed
+without a risk of the object becoming invalid. However, you need to avoid
+pointing to on-stack data like local variables of functions, because these
+are of course destroyed during the `longjmp()`.
- The exception handling code gets a local variable `x` pointing to the
- exception object. When the exception is handled (for example, an error
- message is logged), @trans_caught() is called automatically, which rolls
- back the transaction and frees all its resources. Again, you can use the
- `break` statement to skip this.
+=== Functions and structures ===
- Alternatively, when you are in a nested transaction, you can throw a different
- exception or re-throw the original one. This raises an exception in the
- context of the parent transaction. In this case, the child transaction is
- not rolled back, but its pools are folded as sub-pools of the parent transaction
- and kept until @trans_caught() is called finally.
+!!ucw/trans.h
-* When an exception is thrown outside a transaction, it is converted to
- a plain @die().
+== Exception names [[excnames]] ==
-* Memory management and lifetime of various objects and pools deserve special
- attention, as usually when non-local jumps are taking place. When an exception
- is raised, the exception structure is allocated from the memory pool of the
- current transaction. When the exception is propagated through the stack of
- transactions, no transaction is ever rolled back -- all of them are folded
- and their pools remain accessible until @trans_caught() is called at the end.
- Therefore exceptions can carry pointers to the objects which have failed
- without a risk of the object becoming invalid. However, you need to avoid
- pointing to on-stack data like local variables of functions, because these
- are of course destroyed during the `longjmp()`.
+Exception identifiers form a hierarchy. Each identifier consists of dot-separated
+components with the most general component at the beginning.
-FIXME: Interaction between exceptions, pools and other libucw modules.
+All exceptions raised by LibUCW reside in the `ucw` subtree. So far, the
+following exception types are defined:
-FIXME: Unit tests
+`ucw.fb`:: <<fastbuf:fbexc,Fastbufs>>
-FIXME: Resourcification of more libucw objects
+== FIXME's ==
-FIXME: Do we want to allow res_alloc() when no pool is active.
+- Unit tests
+- Resourcification of more libucw objects.
+- More exceptions
+- Do we want to allow res_alloc() when no pool is active?