Commit 264bde1b authored by Pekka Pessi's avatar Pekka Pessi
Browse files

su_alloc.c: allow reference counting on home objects initialized statically or with su_home_init().

Note that an assertion fails if su_home_deinit() or su_home_destroy() is
passed a home object that has reference count more than 1.

darcs-hash:20070202180709-65a35-8810986f9572370a333dbd0c3bbadd23e9914477.gz
parent 387a895a
......@@ -87,8 +87,8 @@
*
* As you might have guessed, it is also possible to use reference counting
* with home objects. The function su_home_ref() increases the reference
* count, su_home_unref() decreases. A newly allocated home object has
* reference count of 1.
* count, su_home_unref() decreases it. A newly allocated or initialized
* home object has reference count of 1.
*
* @note Please note that while it is possible to create new references to
* secondary home objects which have a parent home, the secondary home
......@@ -104,10 +104,13 @@
* @note
*
* The su_home_destroy() function is deprecated as it does not free the home
* object itself.
* object itself. Like su_home_deinit(), it should be called only on home
* objects with reference count of 1.
*
* The function su_home_init() initializes a home object with infinite
* reference count. It must be deinitialized with su_home_deinit().
* The function su_home_init() initializes a home object structure. When the
* initialized home object is destroyed or deinitialized or its reference
* count reaches zero, the memory allocate thorugh it reclaimed but the home
* object structure itself is not freed.
*
* @section su_home_destructor_usage Destructors
*
......@@ -148,19 +151,20 @@
* @section su_alloc_threadsafe Threadsafe Operation
*
* If multiple threads need to access same home object, it must be marked as
* @c threadsafe by calling su_home_threadsafe() with the home pointer as
* @e threadsafe by calling su_home_threadsafe() with the home pointer as
* argument. 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().
* su_home_mutex_lock() and su_home_mutex_unlock(). These operations are
* no-op on home object that is not threadsafe.
*
* @section su_alloc_preloading Preloading a Memory Home
*
* In some situations there is quite heavy overhead when using the global
* heap allocator. The overhead caused by the large number of small
* In some situations there is quite heavy overhead if the global heap
* allocator is used. The overhead caused by the large number of small
* allocations can be reduced by using su_home_preload(): it allocates or
* preloads some a memory to home to be used as a kind of private heap. The
* preloaded memory area is then used to satisfy small enough allocations.
* preloaded memory area is then used to satisfy small enough allocations.
* For instance, the SIP parser typically preloads some 2K of memory when it
* starts to parse the message.
*
......@@ -172,10 +176,9 @@
* far as possible; if it is not enough, allocation is made from heap.
*
* The word @e auto refers to the automatic scope; however, the home object
* initialized with su_home_auto() must be explicitly deinitialized with
* su_home_deinit() when the program exits the scope where the stack frame
* used in su_home_auto() was allocate.
*
* that was initialized with su_home_auto() must be explicitly deinitialized
* with su_home_deinit() or su_home_unref() when the program exits the scope
* where the stack frame used in su_home_auto() was allocated.
*/
#include <sofia-sip/su_config.h>
......@@ -244,8 +247,9 @@ struct su_block_s {
unsigned sub_prsize:16; /**< Preload size */
unsigned sub_prused:16; /**< Used from preload */
unsigned sub_auto:1; /**< struct su_block_s is from stack! */
unsigned sub_preauto:1; /**< Preload is from stack! */
unsigned sub_hauto:1; /**< "Home" is not from malloc */
unsigned sub_auto:1; /**< struct su_block_s is not from malloc */
unsigned sub_preauto:1; /**< Preload is not from malloc */
unsigned sub_auto_all:1; /**< Everything is from stack! */
unsigned :0;
......@@ -360,8 +364,9 @@ static inline int su_alloc_check(su_block_t const *sub, su_alloc_t const *sua)
/** Allocate the block hash table.
*
* The function su_hash_alloc() allocates a block hash table of @a n
* elements.
* @internal
*
* Allocate a block hash table of @a n elements.
*
* @param home pointer to home object
* @param n number of buckets in hash table
......@@ -374,8 +379,12 @@ static inline su_block_t *su_hash_alloc(size_t n)
{
su_block_t *b = calloc(1, offsetof(su_block_t, sub_nodes[n]));
if (b)
if (b) {
/* Implicit su_home_init(); */
b->sub_ref = 1;
b->sub_hauto = 1;
b->sub_n = n;
}
return b;
}
......@@ -383,6 +392,8 @@ static inline su_block_t *su_hash_alloc(size_t n)
enum sub_zero { do_malloc, do_calloc, do_clone };
/** Allocate a memory block.
*
* @internal
*
* Precondition: locked home
*
......@@ -431,6 +442,7 @@ void *sub_alloc(su_home_t *home,
b2->sub_preload = sub->sub_preload;
b2->sub_prsize = sub->sub_prsize;
b2->sub_prused = sub->sub_prused;
b2->sub_hauto = sub->sub_hauto;
b2->sub_preauto = sub->sub_preauto;
b2->sub_destructor = sub->sub_destructor;
/* auto_all is not copied! */
......@@ -475,7 +487,7 @@ void *sub_alloc(su_home_t *home,
if (!preload)
sub->sub_auto_all = 0;
if (zero == do_clone) {
if (zero >= do_clone) {
/* Prepare cloned home */
su_home_t *subhome = data;
......@@ -487,7 +499,7 @@ void *sub_alloc(su_home_t *home,
subhome->suh_size = (unsigned)size;
subhome->suh_blocks->sub_parent = home;
subhome->suh_blocks->sub_ref = 1;
subhome->suh_blocks->sub_hauto = 0;
}
/* OK, add the block to the hash table. */
......@@ -536,7 +548,7 @@ void *su_home_new(isize_t size)
home->suh_size = (int)size;
home->suh_blocks = su_hash_alloc(SUB_N);
if (home->suh_blocks)
home->suh_blocks->sub_ref = 1;
home->suh_blocks->sub_hauto = 0;
else
free(home), home = NULL;
}
......@@ -614,12 +626,10 @@ int su_home_desctructor(su_home_t *home, void (*destructor)(void *))
/**Unreference a su_home_t object.
*
* The function su_home_unref() decrements the reference count on a home
* object and destroys and frees it and the memory allocations using it.
*
* @param home memory pool object to be unreferences
* Decrements the reference count on home object and destroys and frees it
* and the memory allocations using it if the reference count reaches 0.
*
* The function return values is
* @param home memory pool object to be unreferenced
*
* @retval 1 if object was freed
* @retval 0 if object is still alive
......@@ -652,8 +662,10 @@ int su_home_unref(su_home_t *home)
return 1;
}
else {
int hauto = sub->sub_hauto;
_su_home_deinit(home);
free(home);
if (!hauto)
free(home);
/* UNLOCK(home); */
return 1;
}
......@@ -727,7 +739,7 @@ int su_home_has_parent(su_home_t const *home)
/** Allocate a memory block.
*
* The function su_alloc() allocates a memory block of a given @a size.
* Allocates a memory block of a given @a size.
*
* If @a home is NULL, this function behaves exactly like malloc().
*
......@@ -754,9 +766,9 @@ void *su_alloc(su_home_t *home, isize_t size)
/**Free a memory block.
*
* The function su_free() frees a single memory block. The @a home must be
* the owner of the memory block (usually the memory home used to allocate
* the memory block, or NULL if no home was used).
* Frees a single memory block. The @a home must be the owner of the memory
* block (usually the memory home used to allocate the memory block, or NULL
* if no home was used).
*
* @param home pointer to home object
* @param data pointer to the memory block to be freed
......@@ -812,9 +824,9 @@ void su_free(su_home_t *home, void *data)
/** Check home consistency.
*
* The function su_home_check() ensures that the home structure and all
* memory blocks allocated through it are consistent. It can be used to
* catch memory allocation and usage errors.
* Ensures that the home structure and all memory blocks allocated through
* it are consistent. It can be used to catch memory allocation and usage
* errors.
*
* @param home Pointer to a memory home.
*/
......@@ -853,11 +865,10 @@ void su_home_check_blocks(su_block_t const *b)
/**
* Create an su_home_t object.
*
* The function su_home_create() creates a home object. A home object is
* used to collect multiple memory allocations, so that they all can be
* freed by calling su_home_unref().
* Creates a home object. A home object is used to collect multiple memory
* allocations, so that they all can be freed by calling su_home_unref().
*
* @return This function returns a pointer to an @c su_home_t object, or @c
* @return This function returns a pointer to an #su_home_t object, or
* NULL upon an error.
*/
su_home_t *su_home_create(void)
......@@ -865,10 +876,10 @@ su_home_t *su_home_create(void)
return su_home_new(sizeof(su_home_t));
}
/** Deinitialize a home object
/** Destroy a home object
*
* The function su_home_destroy() frees all memory blocks associated with a
* home object. Note that the home object is not freed.
* Frees all memory blocks associated with a home object. Note that the home
* object structure is not freed.
*
* @param home pointer to a home object
*
......@@ -878,14 +889,22 @@ su_home_t *su_home_create(void)
*/
void su_home_destroy(su_home_t *home)
{
su_home_deinit(home);
/* XXX - home itself is not destroyed */
if (MEMLOCK(home)) {
assert(home->suh_blocks);
assert(home->suh_blocks->sub_ref == 1);
if (!home->suh_blocks->sub_hauto)
/* should warn user */;
home->suh_blocks->sub_hauto = 1;
_su_home_deinit(home);
/* UNLOCK(home); */
}
}
/** Initialize an su_home_t struct.
*
* The function su_home_init() initializes an su_home_t structure. It can be
* used when the home structure is allocated from stack.
* Initializes an su_home_t structure. It can be used when the home
* structure is allocated from stack or when the home structure is part of
* an another object.
*
* @param home pointer to home object
*
......@@ -896,10 +915,14 @@ void su_home_destroy(su_home_t *home)
*/
int su_home_init(su_home_t *home)
{
su_block_t *sub;
if (home == NULL)
return -1;
home->suh_blocks = su_hash_alloc(SUB_N);
home->suh_blocks = sub = su_hash_alloc(SUB_N);
if (!sub)
return -1;
return 0;
}
......@@ -961,9 +984,9 @@ void _su_home_deinit(su_home_t *home)
/** Free memory blocks allocated through home.
*
* The function @c su_home_deinit() frees the memory blocks associated with
* the home object allocated otherwise. It does not free the home object
* itself. Use su_home_unref() to free the home object.
* Frees the memory blocks associated with the home object allocated. It
* does not free the home object itself. Use su_home_unref() to free the
* home object.
*
* @param home pointer to home object
*
......@@ -972,7 +995,9 @@ void _su_home_deinit(su_home_t *home)
void su_home_deinit(su_home_t *home)
{
if (MEMLOCK(home)) {
assert(home->suh_blocks && home->suh_blocks->sub_ref == 0);
assert(home->suh_blocks);
assert(home->suh_blocks->sub_ref == 1);
assert(home->suh_blocks->sub_hauto);
_su_home_deinit(home);
/* UNLOCK(home); */
}
......@@ -980,11 +1005,11 @@ void su_home_deinit(su_home_t *home)
/**Move allocations from a su_home_t object to another.
*
* The function su_home_move() moves allocations made through the @a src
* home object under the @a dst home object. It is handy, for example, if an
* operation allocates some number of blocks that should be freed upon an
* error. It uses a temporary home and moves the blocks from temporary to a
* proper home when successful, but frees the temporary home upon an error.
* Moves allocations made through the @a src home object under the @a dst
* home object. It is handy, for example, if an operation allocates some
* number of blocks that should be freed upon an error. It uses a temporary
* home and moves the blocks from temporary to a proper home when
* successful, but frees the temporary home upon an error.
*
* If @a src has destructor, it is called before starting to move.
*
......@@ -1116,8 +1141,8 @@ void su_home_preload(su_home_t *home, isize_t n, isize_t isize)
/** Preload a memory home from stack.
*
* The function su_home_auto() initalizes a memory home using an area
* allocated from stack. Poor mans alloca().
* Initializes a memory home using an area allocated from stack. Poor man's
* alloca().
*/
su_home_t *su_home_auto(void *area, isize_t size)
{
......@@ -1147,10 +1172,12 @@ su_home_t *su_home_auto(void *area, isize_t size)
size = prepsize + 65535;
sub->sub_n = SUB_N_AUTO;
sub->sub_ref = 1;
sub->sub_preload = p + prepsize;
sub->sub_prsize = (unsigned)(size - prepsize);
sub->sub_preauto = 1;
sub->sub_hauto = 1;
sub->sub_auto = 1;
sub->sub_preauto = 1;
sub->sub_auto_all = 1;
return home;
......@@ -1159,7 +1186,7 @@ su_home_t *su_home_auto(void *area, isize_t size)
/** Reallocate a memory block.
*
* The function su_realloc() allocates a memory block of @a size bytes.
* Allocates a memory block of @a size bytes.
* It copies the old block contents to the new block and frees the old
* block.
*
......@@ -1170,7 +1197,7 @@ su_home_t *su_home_auto(void *area, isize_t size)
* @param size size of the memory block to be allocated
*
* @return
* This function returns a pointer to the allocated memory block or
* A pointer to the allocated memory block or
* NULL if an error occurred.
*/
void *su_realloc(su_home_t *home, void *data, isize_t size)
......@@ -1332,7 +1359,7 @@ int su_in_home(su_home_t *home, void const *memory)
/**Allocate and zero a memory block.
*
* The function su_zalloc() allocates a memory block with a given size from
* Allocates a memory block with a given size from
* given memory home @a home and zeroes the allocated block.
*
* @param home pointer to memory pool object
......@@ -1364,9 +1391,9 @@ void *su_zalloc(su_home_t *home, isize_t size)
/** Allocate a structure
*
* The function su_salloc() allocates a structure with a given size, zeros
* Allocates a structure with a given size, zeros
* it, and initializes the size field to the given size. The size field
* is the first in the structure. It has type of int.
* is an int at the beginning of the structure. Note that it has type of int.
*
* @param home pointer to memory pool object
* @param size size of the structure
......
......@@ -161,9 +161,35 @@ static int test_alloc(void)
su_home_check(h0->home);
su_home_zap(h0->home);
{
su_home_t h1[1];
void test_destructor(void *);
memset(h1, 0, sizeof h1);
TEST(su_home_init(h1), 0);
TEST(su_home_threadsafe(h1), 0);
TEST_1(su_home_ref(h1));
TEST_1(su_home_ref(h1));
TEST(su_home_destructor(h1, test_destructor), 0);
TEST_1(!su_home_unref(h1));
TEST_1(!su_home_unref(h1));
TEST_1(su_home_unref(h1));
TEST(h1->suh_size, 13);
}
END();
}
void test_destructor(void *a)
{
su_home_t *h = a;
h->suh_size = 13;
}
static int test_strdupcat(void)
{
su_home_t home[1] = { SU_HOME_INIT(home) };
......
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