Commit 61f8fb73 authored by Pekka Pessi's avatar Pekka Pessi
Browse files

Added su_home_destructor() to su_alloc.[hc].

darcs-hash:20051223131915-65a35-c690f5c052581d4c69007c1e43f57d0f3008693b.gz
parent 60c34a1a
......@@ -41,7 +41,8 @@
* home is then freed, it will free all blocks to which it has reference.
*
* Typically, there is a @e home @e object which contains a su_home_t
* structure in the beginning of it (sort of inheritance from su_home_t):
* structure in the beginning of the object (sort of inheritance from
* su_home_t):
* @code
* struct context {
* su_home_t ctx_home[1];
......@@ -103,6 +104,15 @@
* function su_home_create() creates a home object with infinite reference
* count. Likewise, su_home_init() does the same.
*
* @section su_home_desctructor_usage Destructors
*
* It is possible to give a destructor function to a home object. The
* destructor releases other resources associated with the home object
* besides memory. The destructor function will be called when the reference
* count of home reaches zero (upon calling su_home_unref()) or the home
* object is otherwise deinitialized (calling su_home_deinit() on
* stack-derived objects).
*
* @section su_home_move_example Combining Allocations
*
* In some cases, an operation that makes multiple memory allocations may
......@@ -136,8 +146,16 @@
* @c threadsafe by calling su_home_threadsafe() with the home pointer as
* argument. A cloned home cannot be marked as threadsafe. The
* threadsafeness is not inherited by clones.
*
* The threadsafe home objects can be locked and unlocked with
* su_home_mutex_lock() and su_home_mutex_unlock().
*/
#include <su_config.h>
#include "su_alloc.h"
#include "su_alloc_stat.h"
#include "su_errno.h"
#include <stdlib.h>
#include <stddef.h>
#include <memory.h>
......@@ -145,10 +163,6 @@
#include <assert.h>
#include <su_config.h>
#include "su_alloc.h"
#include "su_alloc_stat.h"
void (*su_home_locker)(void *mutex);
void (*su_home_unlocker)(void *mutex);
......@@ -192,7 +206,7 @@ struct su_block_s {
su_home_t *sub_parent; /**< Parent home */
char *sub_preload; /**< Preload area */
su_home_stat_t *sub_stats; /**< Statistics.. */
void (*sub_destructor)(void *); /**< Destructor function */
unsigned sub_ref; /**< Reference count */
unsigned sub_used; /**< Number of blocks allocated */
unsigned sub_n; /**< Size of hash table */
......@@ -502,10 +516,32 @@ void *su_home_ref(su_home_t *home)
sub->sub_ref++;
UNLOCK(home);
}
else
su_seterrno(EFAULT);
return home;
}
/** Set destructor function */
SU_DLL int su_home_desctructor(su_home_t *home, void (*destructor)(void *))
{
int retval = -1;
if (home) {
su_block_t *sub = MEMLOCK(home);
if (sub && sub->sub_destructor == NULL) {
sub->sub_destructor = destructor;
retval = 0;
}
UNLOCK(home);
}
else
su_seterrno(EFAULT);
return retval;
}
/**Unreference a su_home_t object.
*
* The function su_home_unref() decrements the reference count on a home
......@@ -776,6 +812,12 @@ void _su_home_deinit(su_home_t *home)
unsigned i;
su_block_t *b;
if (home->suh_blocks->sub_destructor) {
void (*destructor)(void *) = home->suh_blocks->sub_destructor;
home->suh_blocks->sub_destructor = NULL;
destructor(home);
}
b = home->suh_blocks;
su_home_check_blocks(b);
......@@ -1212,7 +1254,7 @@ void *su_salloc(su_home_t *home, int size)
int su_home_mutex_lock(su_home_t *home)
{
if (home == NULL)
return -1;
return su_seterrno(EFAULT);
if (home->suh_lock) {
su_home_ref(home);
......@@ -1230,7 +1272,7 @@ int su_home_mutex_lock(su_home_t *home)
int su_home_mutex_unlock(su_home_t *home)
{
if (home == NULL)
return -1;
return su_seterrno(EFAULT);
if (home->suh_lock) {
su_home_mutex_unlocker(home->suh_lock);
......@@ -1246,9 +1288,14 @@ int su_home_mutex_unlock(su_home_t *home)
/** Initialize statistics structure */
void su_home_init_stats(su_home_t *home)
{
su_block_t *sub = home->suh_blocks;
su_block_t *sub;
int size;
if (home == NULL)
return;
sub = home->suh_blocks;
if (!sub)
sub = home->suh_blocks = su_hash_alloc(SUB_N);
if (!sub)
......
......@@ -65,14 +65,10 @@ SU_DLL void *su_home_new(int size)
SU_DLL void *su_home_ref(su_home_t *);
SU_DLL void su_home_unref(su_home_t *);
SU_DLL void *su_home_clone(su_home_t *parent, int size)
__attribute__((__malloc__));
#define su_home_zap(h) su_home_unref((h))
SU_DLL int su_home_desctructor(su_home_t *, void (*)(void *));
SU_DLL su_home_t *su_home_create(void)
SU_DLL void *su_home_clone(su_home_t *parent, int size)
__attribute__((__malloc__));
SU_DLL void su_home_destroy(su_home_t *h);
SU_DLL int su_home_init(su_home_t *h);
SU_DLL void su_home_deinit(su_home_t *h);
......@@ -124,4 +120,13 @@ SU_DLL char *su_vsprintf(su_home_t *home, char const *fmt, va_list ap)
/* free an independent block */
SU_DLL void su_free(su_home_t *h, void *);
/* ---------------------------------------------------------------------- */
/* Deprecated */
SU_DLL su_home_t *su_home_create(void)
__attribute__((__malloc__));
SU_DLL void su_home_destroy(su_home_t *h);
#define su_home_zap(h) su_home_unref((h))
#endif /* ! defined(SU_ALLOC_H) */
......@@ -34,6 +34,7 @@
#include "config.h"
#include <su_alloc.h>
#include <su.h>
#if SU_HAVE_PTHREADS
#include <pthread.h>
......@@ -57,28 +58,34 @@ static void mutex_unlocker(void *_mutex)
pthread_mutex_t *mutex = _mutex;
pthread_mutex_unlock(mutex + 1);
}
#endif
/** Convert su_home_t object to a thread-safe one.
*
* The function su_home_threadsafe() converts a memory home object
* as thread-safe.
*
* Convert a memory home object as thread-safe by allocating mutexes and
* modifying function pointers in su_alloc.c module.
* @param home memory home object to be converted thread-safe.
*
* @return The function su_home_threadsafe() return 0 when successful,
* or -1 upon an error.
* @retval 0 when successful,
* @retval -1 upon an error.
*/
int su_home_threadsafe(su_home_t *home)
{
pthread_mutex_t *mutex;
if (home == NULL || home->suh_lock)
if (home == NULL)
return su_seterrno(EFAULT);
if (home->suh_lock) /* Already? */
return 0;
assert(!su_home_has_parent(home));
if (su_home_has_parent(home))
return -1;
return su_seterrno(EINVAL);
#if SU_HAVE_PTHREADS
if (!su_home_unlocker) {
/* Avoid linking pthread library just for memory management */
su_home_mutex_locker = mutex_locker;
......@@ -88,6 +95,7 @@ int su_home_threadsafe(su_home_t *home)
}
mutex = su_alloc(home, 2 * sizeof (pthread_mutex_t));
assert(mutex);
if (mutex) {
/* Mutex for memory operations */
pthread_mutex_init(mutex, NULL);
......@@ -96,14 +104,9 @@ int su_home_threadsafe(su_home_t *home)
home->suh_lock = (void *)mutex;
return 0;
}
assert(mutex);
return -1;
}
#else
int su_home_threadsafe(su_home_t *h)
{
su_seterrno(ENOSYS);
#endif
return -1;
}
#endif
......@@ -55,43 +55,63 @@ static int test_strlst(void);
static int test_vectors(void);
static int test_auto(void);
/** Test tl_list and tl_dup */
/* Type derived from home */
typedef struct { su_home_t home[1]; int *p; } exhome_t;
void exdestructor(void *arg)
{
exhome_t *ex = arg;
(*ex->p)++;
}
/** Test basic memory home operations */
int test_alloc(void)
{
su_home_t *h0, *h1, *h2, *h3;
exhome_t *h0, *h1, *h2, *h3;
su_home_t home[1] = { SU_HOME_INIT(home) };
enum { N = 40 };
void *m0[N], *m1[N], *m;
char *c, *c0, *p0, *p1;
int i;
int d0, d1, d2, d3;
BEGIN();
TEST_1(h0 = su_home_new(sizeof(*h0)));
TEST_1(h1 = su_home_clone(h0, sizeof(*h1)));
TEST_1(h2 = su_home_ref(h0));
su_home_unref(h0);
su_home_unref(h2);
TEST_1(h1 = su_home_clone(h0->home, sizeof(*h1)));
d0 = d1 = d2 = d3 = 0;
h0->p = &d0; h1->p = &d1;
TEST(su_home_desctructor(h0->home, exdestructor), 0);
TEST(su_home_desctructor(h1->home, exdestructor), 0);
TEST_1(h2 = su_home_ref(h0->home));
su_home_unref(h0->home);
TEST(d0, 0);
su_home_unref(h2->home); /* Should call destructor of cloned home, too */
TEST(d0, 1);
TEST(d1, 1);
TEST_1(h0 = su_home_new(sizeof(*h0)));
TEST_1(h1 = su_home_clone(h0, sizeof(*h1)));
TEST_1(h2 = su_home_clone(h1, sizeof(*h2)));
TEST_1(h3 = su_home_clone(h2, sizeof(*h3)));
TEST_1(h1 = su_home_clone(h0->home, sizeof(*h1)));
TEST_1(h2 = su_home_clone(h1->home, sizeof(*h2)));
TEST_1(h3 = su_home_clone(h2->home, sizeof(*h3)));
TEST(su_home_threadsafe(h0), 0);
TEST(su_home_threadsafe(h0->home), 0);
for (i = 0; i < N; i++) {
TEST_1(m0[i] = su_zalloc(h3, 20));
TEST_1(m1[i] = su_zalloc(h2, 20));
TEST_1(m0[i] = su_zalloc(h3->home, 20));
TEST_1(m1[i] = su_zalloc(h2->home, 20));
}
TEST_1(m = su_zalloc(h2, 20));
TEST_1(m = su_zalloc(h2->home, 20));
TEST(su_home_move(home, NULL), 0);
TEST(su_home_move(NULL, home), 0);
TEST(su_home_move(home, h3), 0);
TEST(su_home_move(h2, h3), 0);
TEST(su_home_move(h1, h2), 0);
TEST(su_home_move(home, h3->home), 0);
TEST(su_home_move(h2->home, h3->home), 0);
TEST(su_home_move(h1->home, h2->home), 0);
su_home_preload(home, 1, 1024 + 2 * 8);
......@@ -113,10 +133,10 @@ int test_alloc(void)
su_home_check(home);
su_home_deinit(home);
su_home_check(h0);
su_home_zap(h2);
su_home_check(h0);
su_home_zap(h0);
su_home_check(h2->home);
su_home_zap(h2->home);
su_home_check(h0->home);
su_home_zap(h0->home);
END();
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment