Transactions and resource tracking
==================================
+LibUCW is equipped with a general system for keeping track of resources
+(allocated memory, open files, ...) and freeing them when requested to.
+
+The resource tracker can be used either separately (in the form of explicitly
+managed resource pools) or within a transactional layer, which offers
+exceptions similar to those in higher-level languages. An exception
+then rolls back the transaction, freeing all temporary resources allocated
+within the transaction.
+
+Resource pools: ucw/respool.h
+-----------------------------
+
+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
+resources are freed in the opposite order, which allows a resource
+refer to data of previously created resources.
+
+A resource can be also freed separately (which unlinks it from the pool),
+or *detached* from the pool (which keeps the real resource, but forgets
+its meta-data, so the resource is no longer tracked).
+
+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
+operating on resources work on both active and in-active pools.
+
+!!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.
+ 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
are of course destroyed during the `longjmp()`.
FIXME: Interaction between exceptions, pools and other libucw modules.
+
+FIXME: Unit tests
+
+FIXME: Resourcification of more libucw objects
+
+FIXME: Do we want to allow res_alloc() when no pool is active.
* of the GNU Lesser General Public License.
*/
-/*
- * FIXME:
- * - check other candidates for resourcification
- * - unit tests
- * - automatic freeing of trans pool on thread exit
- */
-
#ifndef _UCW_RESPOOL_H
#define _UCW_RESPOOL_H
#include "ucw/clists.h"
#include "ucw/threads.h"
+/**
+ * A resource pool. It contains a name of the pool (which is printed
+ * in all debugging dumps, otherwise it is not used) and a bunch of
+ * fields for internal use.
+ **/
struct respool {
clist resources;
const char *name;
struct resource *subpool_of;
};
+/**
+ * Each resource is represented by this structure. It is linked to a resource
+ * pool it belongs to. It contains a pointer to a resource class (which describes how to
+ * handle the resource) and data private to the resource class.
+ **/
struct resource {
cnode n;
struct respool *rpool;
// More data specific for the particular class can follow
};
-struct res_class {
- const char *name;
- void (*detach)(struct resource *r);
- void (*free)(struct resource *r);
- void (*dump)(struct resource *r, uns indent);
- uns res_size; // Size of the resource structure (0=default)
-};
-
+/**
+ * Creates a new resource pool. If a memory pool is given, meta-data of all resources
+ * will be allocated from this pool. Otherwise, they will be malloc'ed.
+ **/
struct respool *rp_new(const char *name, struct mempool *mp);
-void rp_delete(struct respool *rp);
-void rp_detach(struct respool *rp);
-void rp_dump(struct respool *rp, uns indent);
-static inline struct respool *
-rp_current(void)
+void rp_delete(struct respool *rp); /** Deletes a resource pool, freeing all resources. **/
+void rp_detach(struct respool *rp); /** Deletes a resource pool, detaching all resources. **/
+void rp_dump(struct respool *rp, uns indent); /** Prints out a debugging dump of a pool to stdout. **/
+
+/** Returns a pointer to the currently active resource pool or NULL, if none exists. **/
+static inline struct respool *rp_current(void)
{
- return ucwlib_thread_context()->current_respool; // May be NULL
+ return ucwlib_thread_context()->current_respool;
}
-static inline struct respool *
-rp_switch(struct respool *rp)
+/**
+ * Makes the given resource pool active; returns a pointer to the previously active pool
+ * or NULL, if there was none. Calling with @rp equal to NULL deactivates the pool.
+ **/
+static inline struct respool *rp_switch(struct respool *rp)
{
struct ucwlib_context *ctx = ucwlib_thread_context();
struct respool *orp = ctx->current_respool;
}
struct resource *res_alloc(const struct res_class *rc) LIKE_MALLOC; // Returns NULL if there is no pool active
-void res_drop(struct resource *r);
+
+void res_dump(struct resource *r, uns indent); /** Prints out a debugging dump of the resource to stdout. **/
+void res_free(struct resource *r); /** Frees a resource, unlinking it from its pool. **/
+
+/***
+ * === Resource classes
+ *
+ * A resource class describes how to handle a particular type of resources.
+ * Most importantly, it defines a set of callbacks for performing operations
+ * on the resources:
+ *
+ * * dump() should print a description of the resource used for debugging
+ * to the standard output. The description should end with a newline character
+ * and in case of a multi-line description, the subsequent lines should be
+ * indented by @indent spaces.
+ * * free() frees the resource; the struct resource is freed automatically afterwards.
+ * * detach() breaks the link between the struct resource and the real resource;
+ * the struct resource is freed automatically afterwards, while the resource
+ * continues to live.
+ *
+ * The following functions are intended for use by the resource classes only.
+ ***/
+
+/** The structure describing a resource class. **/
+struct res_class {
+ const char *name; // The name of the class (included in debugging dumps)
+ void (*detach)(struct resource *r); // The callbacks
+ void (*free)(struct resource *r);
+ void (*dump)(struct resource *r, uns indent);
+ uns res_size; // Size of the resource structure (0=default)
+};
+
+/** Unlinks a resource from a pool and releases its meta-data. However, the resource itself is kept. **/
void res_detach(struct resource *r);
-void res_free(struct resource *r);
-void res_dump(struct resource *r, uns indent);
-static inline struct resource * // Returns NULL if there is no pool active
-res_new(const struct res_class *rc, void *priv)
+/**
+ * Unlinks a resource from a pool and releases its meta-data. Unlike @res_detach(),
+ * it does not invoke any callbacks.
+ **/
+void res_drop(struct resource *r);
+
+/**
+ * Creates a new resource of the specific class, setting its private data to @priv.
+ * Returns NULL if there is no resource pool active.
+ **/
+static inline struct resource *res_new(const struct res_class *rc, void *priv)
{
struct resource *r = res_alloc(rc);
if (r)
return r;
}
-/* Various special types of resources */
+/***
+ * === Pre-defined resource classes
+ ***/
-struct resource *res_for_fd(int fd); // Creates a resource that closes a given file descriptor
+struct resource *res_for_fd(int fd); /** Creates a resource that closes a given file descriptor. **/
-void *res_malloc(size_t size, struct resource **ptr) LIKE_MALLOC; // Allocates memory and creates a resource for it
-void *res_malloc_zero(size_t size, struct resource **ptr) LIKE_MALLOC; // Allocates zero-initialized memory and creates a resource for it
-void *res_realloc(struct resource *res, size_t size);
+void *res_malloc(size_t size, struct resource **ptr) LIKE_MALLOC; /** Allocates memory and creates a resource for it. **/
+void *res_malloc_zero(size_t size, struct resource **ptr) LIKE_MALLOC; /** Allocates zero-initialized memory and creates a resource for it. **/
+void *res_realloc(struct resource *res, size_t size); /** Re-allocates memory obtained by @res_malloc() or @res_malloc_zero(). **/
-struct resource *res_subpool(struct respool *rp); // Make @rp a sub-pool of the current pool
+/**
+ * Converts the resource pool @rp to a resource inside the current resource pool (i.e., its sub-pool).
+ * You can delete the sub-pool either by freeing this resource, or by calling
+ * @rp_delete() on it, which removes the resource automatically.
+ **/
+struct resource *res_subpool(struct respool *rp);
#endif