WIP: pooled allocator, initial version
* creating and allocating, freeing elements * live objects will be destructed when a mpool gets destroyed and a destructor was set up
This commit is contained in:
parent
062dbfe82f
commit
2a9d59ccd0
7 changed files with 905 additions and 0 deletions
|
|
@ -89,6 +89,7 @@ NOBUG_CPP_DEFINE_FLAG_PARENT ( proc_dbg, debugging);
|
||||||
NOBUG_CPP_DEFINE_FLAG_PARENT ( gui_dbg, debugging);
|
NOBUG_CPP_DEFINE_FLAG_PARENT ( gui_dbg, debugging);
|
||||||
/** base if debug logging for the support library */
|
/** base if debug logging for the support library */
|
||||||
NOBUG_CPP_DEFINE_FLAG_PARENT ( library_dbg, debugging);
|
NOBUG_CPP_DEFINE_FLAG_PARENT ( library_dbg, debugging);
|
||||||
|
NOBUG_CPP_DEFINE_FLAG_PARENT ( mpool_dbg, library_dbg);
|
||||||
NOBUG_CPP_DEFINE_FLAG_PARENT ( psplay_dbg, library_dbg);
|
NOBUG_CPP_DEFINE_FLAG_PARENT ( psplay_dbg, library_dbg);
|
||||||
NOBUG_CPP_DEFINE_FLAG_PARENT ( resourcecollector_dbg, library_dbg);
|
NOBUG_CPP_DEFINE_FLAG_PARENT ( resourcecollector_dbg, library_dbg);
|
||||||
NOBUG_CPP_DEFINE_FLAG_PARENT ( mutex_dbg, library_dbg);
|
NOBUG_CPP_DEFINE_FLAG_PARENT ( mutex_dbg, library_dbg);
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ liblumiera_la_CXXFLAGS = $(AM_CXXFLAGS) -Wall -Wextra
|
||||||
|
|
||||||
liblumiera_la_SOURCES = \
|
liblumiera_la_SOURCES = \
|
||||||
$(liblumiera_la_srcdir)/error.c \
|
$(liblumiera_la_srcdir)/error.c \
|
||||||
|
$(liblumiera_la_srcdir)/mpool.c \
|
||||||
$(liblumiera_la_srcdir)/exception.cpp \
|
$(liblumiera_la_srcdir)/exception.cpp \
|
||||||
$(liblumiera_la_srcdir)/mutex.c \
|
$(liblumiera_la_srcdir)/mutex.c \
|
||||||
$(liblumiera_la_srcdir)/recmutex.c \
|
$(liblumiera_la_srcdir)/recmutex.c \
|
||||||
|
|
@ -51,6 +52,7 @@ liblumiera_la_SOURCES = \
|
||||||
noinst_HEADERS += \
|
noinst_HEADERS += \
|
||||||
$(liblumiera_la_srcdir)/error.h \
|
$(liblumiera_la_srcdir)/error.h \
|
||||||
$(liblumiera_la_srcdir)/error.hpp \
|
$(liblumiera_la_srcdir)/error.hpp \
|
||||||
|
$(liblumiera_la_srcdir)/mpool.h \
|
||||||
$(liblumiera_la_srcdir)/mutex.h \
|
$(liblumiera_la_srcdir)/mutex.h \
|
||||||
$(liblumiera_la_srcdir)/recmutex.h \
|
$(liblumiera_la_srcdir)/recmutex.h \
|
||||||
$(liblumiera_la_srcdir)/rwlock.h \
|
$(liblumiera_la_srcdir)/rwlock.h \
|
||||||
|
|
|
||||||
487
src/lib/mpool.c
Normal file
487
src/lib/mpool.c
Normal file
|
|
@ -0,0 +1,487 @@
|
||||||
|
/*
|
||||||
|
mpool.h - memory pool for constant sized objects
|
||||||
|
|
||||||
|
Copyright (C) Lumiera.org
|
||||||
|
2009, Christian Thaeter <ct@pipapo.org>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include "mpool.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Cluster and node structures are private
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct mpoolcluster_struct mpoolcluster;
|
||||||
|
typedef mpoolcluster* MPoolcluster;
|
||||||
|
typedef const mpoolcluster* const_MPoolcluster;
|
||||||
|
|
||||||
|
struct mpoolcluster_struct
|
||||||
|
{
|
||||||
|
llist node; /* all clusters */
|
||||||
|
void* data[]; /* bitmap and elements */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct mpoolnode_struct mpoolnode;
|
||||||
|
typedef mpoolnode* MPoolnode;
|
||||||
|
typedef const mpoolnode* const_MPoolnode;
|
||||||
|
|
||||||
|
struct mpoolnode_struct
|
||||||
|
{
|
||||||
|
llist node;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
MPool
|
||||||
|
mpool_init (MPool self, size_t elem_size, unsigned elements_per_cluster, mpool_destroy_fn dtor)
|
||||||
|
{
|
||||||
|
TRACE (mpool_dbg, "%p: elem_size %zd: elem_per_cluster %u", self, elem_size, elements_per_cluster);
|
||||||
|
|
||||||
|
if (self)
|
||||||
|
{
|
||||||
|
llist_init (&self->freelist);
|
||||||
|
llist_init (&self->clusters);
|
||||||
|
self->elem_size = (elem_size+sizeof(void*)-1) / sizeof(void*) * sizeof(void*); /* void* aligned */
|
||||||
|
|
||||||
|
/* minimum size is the size of a llist node */
|
||||||
|
if (self->elem_size < sizeof(llist))
|
||||||
|
self->elem_size = sizeof(llist);
|
||||||
|
|
||||||
|
self->elements_per_cluster = elements_per_cluster;
|
||||||
|
|
||||||
|
self->elements_free = 0;
|
||||||
|
self->destroy = dtor;
|
||||||
|
self->locality = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define MPOOL_BITMAP_SIZE(elements_per_cluster) \
|
||||||
|
(((elements_per_cluster) + sizeof(uintptr_t)*CHAR_BIT - 1) \
|
||||||
|
/ (sizeof(uintptr_t) * CHAR_BIT) * sizeof (uintptr_t))
|
||||||
|
|
||||||
|
|
||||||
|
static inline void*
|
||||||
|
cluster_element_get (MPoolcluster cluster, MPool self, unsigned n)
|
||||||
|
{
|
||||||
|
return (void*)cluster + /* start address */
|
||||||
|
sizeof (*cluster) + /* header */
|
||||||
|
MPOOL_BITMAP_SIZE (self->elements_per_cluster) + /* bitmap */
|
||||||
|
self->elem_size * n; /* offset*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
bitmap_bit_get_nth (MPoolcluster cluster, unsigned index)
|
||||||
|
{
|
||||||
|
TRACE (mpool_dbg, "cluster %p: index %u", cluster, index);
|
||||||
|
|
||||||
|
ldiv_t div = ldiv(index, sizeof(uintptr_t)*CHAR_BIT);
|
||||||
|
uintptr_t* bitmap = (uintptr_t*)&cluster->data;
|
||||||
|
|
||||||
|
return bitmap[div.quot] & ((uintptr_t)1<<div.rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MPool
|
||||||
|
mpool_destroy (MPool self)
|
||||||
|
{
|
||||||
|
TRACE (mpool_dbg, "%p", self);
|
||||||
|
if (self)
|
||||||
|
{
|
||||||
|
LLIST_WHILE_TAIL(&self->clusters, cluster)
|
||||||
|
{
|
||||||
|
if (self->destroy)
|
||||||
|
for (unsigned i = 0; i < self->elements_per_cluster; ++i)
|
||||||
|
{
|
||||||
|
if (bitmap_bit_get_nth ((MPoolcluster)cluster, i))
|
||||||
|
{
|
||||||
|
void* obj = cluster_element_get ((MPoolcluster)cluster, self, i);
|
||||||
|
TRACE (mpool_dbg, "dtor: cluster %p: obj %p: freelist %p", cluster, obj, self->freelist);
|
||||||
|
self->destroy (obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
llist_unlink_fast_ (cluster);
|
||||||
|
TRACE (mpool_dbg, "freeing cluster %p" , cluster);
|
||||||
|
free (cluster);
|
||||||
|
}
|
||||||
|
|
||||||
|
llist_init (&self->freelist);
|
||||||
|
self->elements_free = 0;
|
||||||
|
self->locality = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
MPool
|
||||||
|
mpool_cluster_alloc_ (MPool self)
|
||||||
|
{
|
||||||
|
MPoolcluster cluster = malloc (sizeof (*cluster) + /* header */
|
||||||
|
MPOOL_BITMAP_SIZE (self->elements_per_cluster) + /* bitmap */
|
||||||
|
self->elem_size * self->elements_per_cluster); /* elements */
|
||||||
|
|
||||||
|
TRACE (mpool_dbg, "%p", cluster);
|
||||||
|
|
||||||
|
if (!cluster)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* clear the bitmap */
|
||||||
|
memset (&cluster->data, 0, MPOOL_BITMAP_SIZE (self->elements_per_cluster));
|
||||||
|
|
||||||
|
/* initialize freelist */
|
||||||
|
for (unsigned i = 0; i < self->elements_per_cluster; ++i)
|
||||||
|
{
|
||||||
|
MPoolnode node = cluster_element_get (cluster, self, i);
|
||||||
|
TRACE (mpool_dbg, "node %p", node);
|
||||||
|
llist_insert_tail (&self->freelist, llist_init (&node->node));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we insert the cluster at head because its likely be used next */
|
||||||
|
llist_insert_head (&self->clusters, llist_init (&cluster->node));
|
||||||
|
|
||||||
|
self->elements_free += self->elements_per_cluster;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
cmp_cluster_contains_element (const_LList cluster, const_LList element, void* self)
|
||||||
|
{
|
||||||
|
if (element < cluster)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if ((void*)element >
|
||||||
|
(void*)cluster +
|
||||||
|
sizeof (*cluster) + /* header */
|
||||||
|
MPOOL_BITMAP_SIZE (((MPool)self)->elements_per_cluster) + /* bitmap */
|
||||||
|
((MPool)self)->elem_size * ((MPool)self)->elements_per_cluster) /* elements */
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline MPoolcluster
|
||||||
|
element_cluster_get (MPool self, void* element)
|
||||||
|
{
|
||||||
|
return (MPoolcluster) llist_ufind (&self->clusters, (const_LList) element, cmp_cluster_contains_element, self);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline unsigned
|
||||||
|
uintptr_nearestbit (uintptr_t v, unsigned n)
|
||||||
|
{
|
||||||
|
unsigned r = 0;
|
||||||
|
uintptr_t mask = 1ULL<<n;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
if (v&mask)
|
||||||
|
{
|
||||||
|
if (v&mask& ~(~0ULL<<n))
|
||||||
|
return n-r;
|
||||||
|
else
|
||||||
|
return n+r;
|
||||||
|
}
|
||||||
|
if (mask == ~0ULL)
|
||||||
|
return ~0U;
|
||||||
|
++r;
|
||||||
|
mask |= ((mask<<1)|(mask>>1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static inline void*
|
||||||
|
alloc_near (MPoolcluster cluster, MPool self, void* locality)
|
||||||
|
{
|
||||||
|
void* begin_of_elements =
|
||||||
|
(void*)cluster +
|
||||||
|
sizeof (*cluster) + /* header */
|
||||||
|
MPOOL_BITMAP_SIZE (((MPool)self)->elements_per_cluster); /* bitmap */
|
||||||
|
|
||||||
|
#if UINTPTR_MAX > 4294967295U /* 64 bit */
|
||||||
|
lldiv_t div = lldiv((locality - begin_of_elements) / self->elem_size, sizeof(uintptr_t)*CHAR_BIT);
|
||||||
|
#else /* 32 bit */
|
||||||
|
ldiv_t div = ldiv((locality - begin_of_elements) / self->elem_size, sizeof(uintptr_t)*CHAR_BIT);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uintptr_t* bitmap = (uintptr_t*)&cluster->data;
|
||||||
|
unsigned r = ~0U;
|
||||||
|
|
||||||
|
TRACE (mpool_dbg, "cluster %p: bitmap %p %p: elements %p: index %d", cluster, bitmap, bitmap[div.quot], begin_of_elements, div.quot);
|
||||||
|
|
||||||
|
/* the bitmap word at locality */
|
||||||
|
if (bitmap[div.quot] < UINTPTR_MAX)
|
||||||
|
{
|
||||||
|
r = uintptr_nearestbit (~bitmap[div.quot], div.rem);
|
||||||
|
}
|
||||||
|
/* the bitmap word before locality, this gives a slight bias towards the begin, keeping the pool compact */
|
||||||
|
else if (div.quot && bitmap[div.quot-1] < UINTPTR_MAX)
|
||||||
|
{
|
||||||
|
--div.quot;
|
||||||
|
r = uintptr_nearestbit (~bitmap[div.quot], sizeof(uintptr_t)*CHAR_BIT-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r != ~0U)
|
||||||
|
{
|
||||||
|
void* ret = begin_of_elements + ((uintptr_t)(div.quot*sizeof(uintptr_t)*CHAR_BIT+r)*self->elem_size);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
bitmap_set_element (MPoolcluster cluster, MPool self, void* element)
|
||||||
|
{
|
||||||
|
void* begin_of_elements =
|
||||||
|
(void*)cluster +
|
||||||
|
sizeof (*cluster) + /* header */
|
||||||
|
MPOOL_BITMAP_SIZE (((MPool)self)->elements_per_cluster); /* bitmap */
|
||||||
|
|
||||||
|
#if UINTPTR_MAX > 4294967295U /* 64 bit */
|
||||||
|
lldiv_t div = lldiv((element - begin_of_elements) / self->elem_size, sizeof(uintptr_t)*CHAR_BIT);
|
||||||
|
#else /* 32 bit */
|
||||||
|
ldiv_t div = ldiv((element - begin_of_elements) / self->elem_size, sizeof(uintptr_t)*CHAR_BIT);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uintptr_t* bitmap = (uintptr_t*)&cluster->data;
|
||||||
|
bitmap[div.quot] |= ((uintptr_t)1<<div.rem);
|
||||||
|
|
||||||
|
TRACE (mpool_dbg, "set bit %d, index %d, of %p is %p", div.rem, div.quot, element, bitmap[div.quot]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
bitmap_clear_element (MPoolcluster cluster, MPool self, void* element)
|
||||||
|
{
|
||||||
|
void* begin_of_elements =
|
||||||
|
(void*)cluster +
|
||||||
|
sizeof (*cluster) + /* header */
|
||||||
|
MPOOL_BITMAP_SIZE (((MPool)self)->elements_per_cluster); /* bitmap */
|
||||||
|
|
||||||
|
#if UINTPTR_MAX > 4294967295U /* 64 bit */
|
||||||
|
lldiv_t div = lldiv((element - begin_of_elements) / self->elem_size, sizeof(uintptr_t)*CHAR_BIT);
|
||||||
|
#else /* 32 bit */
|
||||||
|
ldiv_t div = ldiv((element - begin_of_elements) / self->elem_size, sizeof(uintptr_t)*CHAR_BIT);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uintptr_t* bitmap = (uintptr_t*)&cluster->data;
|
||||||
|
bitmap[div.quot] &= ~((uintptr_t)1<<div.rem);
|
||||||
|
|
||||||
|
TRACE (mpool_dbg, "clear bit %d, index %d, of %p is %p", div.rem, div.quot, element, bitmap[div.quot]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void*
|
||||||
|
mpool_alloc (MPool self)
|
||||||
|
{
|
||||||
|
if (!self->elements_free)
|
||||||
|
{
|
||||||
|
if (mpool_cluster_alloc_ (self))
|
||||||
|
{
|
||||||
|
self->locality = NULL; /* supress alloc_near() */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ERROR (mpool_dbg, "allocation failure");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void* ret = NULL;
|
||||||
|
|
||||||
|
if (self->locality)
|
||||||
|
{
|
||||||
|
ret = alloc_near (element_cluster_get (self, self->locality), self, self->locality);
|
||||||
|
TRACE_IF (ret, mpool_dbg, "near allocation %p", ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
ret = llist_head (&self->freelist);
|
||||||
|
TRACE_IF (ret, mpool_dbg, "far allocation %p", ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
llist_unlink_fast_ ((LList)ret);
|
||||||
|
bitmap_set_element (element_cluster_get (self, ret), self, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE (mpool_dbg, "%p", ret);
|
||||||
|
self->locality = ret;
|
||||||
|
--self->elements_free;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
put a element back on the pool
|
||||||
|
*/
|
||||||
|
static inline MPoolnode
|
||||||
|
find_near (MPoolcluster cluster, MPool self, void* element)
|
||||||
|
{
|
||||||
|
void* begin_of_elements =
|
||||||
|
(void*)cluster +
|
||||||
|
sizeof (*cluster) + /* header */
|
||||||
|
MPOOL_BITMAP_SIZE (((MPool)self)->elements_per_cluster); /* bitmap */
|
||||||
|
|
||||||
|
uintptr_t index = (element - begin_of_elements) / self->elem_size;
|
||||||
|
|
||||||
|
#if UINTPTR_MAX > 4294967295U /* 64 bit */
|
||||||
|
lldiv_t div = lldiv (index, sizeof(uintptr_t)*CHAR_BIT);
|
||||||
|
#else /* 32 bit */
|
||||||
|
ldiv_t div = ldiv (index, sizeof(uintptr_t)*CHAR_BIT);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uintptr_t* bitmap = (uintptr_t*)&cluster->data;
|
||||||
|
unsigned r = ~0U;
|
||||||
|
|
||||||
|
TRACE (mpool_dbg, "cluster %p: bitmap %p %p: elements %p: index %d", cluster, bitmap, bitmap[div.quot], begin_of_elements, div.quot);
|
||||||
|
|
||||||
|
/* the bitmap word at locality */
|
||||||
|
if (bitmap[div.quot] < UINTPTR_MAX)
|
||||||
|
{
|
||||||
|
r = uintptr_nearestbit (~bitmap[div.quot], div.rem);
|
||||||
|
}
|
||||||
|
/* the bitmap word after element, we assume that elements after the searched element are more likely be free */
|
||||||
|
else if (index < self->elements_per_cluster && bitmap[div.quot+1] < UINTPTR_MAX)
|
||||||
|
{
|
||||||
|
++div.quot;
|
||||||
|
r = uintptr_nearestbit (~bitmap[div.quot], 0);
|
||||||
|
}
|
||||||
|
/* finally the bitmap word before element */
|
||||||
|
else if (index > 0 && bitmap[div.quot-1] < UINTPTR_MAX)
|
||||||
|
{
|
||||||
|
--div.quot;
|
||||||
|
r = uintptr_nearestbit (~bitmap[div.quot], sizeof(uintptr_t)*CHAR_BIT-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r != ~0U)
|
||||||
|
return begin_of_elements + ((uintptr_t)(div.quot*sizeof(uintptr_t)*CHAR_BIT+r)*self->elem_size);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mpool_free (MPool self, void* element)
|
||||||
|
{
|
||||||
|
if (self && element)
|
||||||
|
{
|
||||||
|
TRACE (mpool_dbg, "mpool %p: element %p", self, element);
|
||||||
|
|
||||||
|
MPoolcluster cluster = element_cluster_get (self,element);
|
||||||
|
|
||||||
|
bitmap_clear_element (cluster, self, element);
|
||||||
|
llist_init (&((MPoolnode)element)->node);
|
||||||
|
|
||||||
|
MPoolnode near = find_near (cluster, self, element);
|
||||||
|
TRACE (mpool_dbg, "near %p", near);
|
||||||
|
|
||||||
|
if (near)
|
||||||
|
{
|
||||||
|
if (near < (MPoolnode)element)
|
||||||
|
llist_insert_next (&near->node, &((MPoolnode)element)->node);
|
||||||
|
else
|
||||||
|
llist_insert_prev (&near->node, &((MPoolnode)element)->node);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
llist_insert_tail (&self->freelist, &((MPoolnode)element)->node);
|
||||||
|
|
||||||
|
bitmap_clear_element (cluster, self, element);
|
||||||
|
|
||||||
|
++self->elements_free;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned
|
||||||
|
mpool_available (MPool self)
|
||||||
|
{
|
||||||
|
return self->elements_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MPool
|
||||||
|
mpool_reserve (MPool self, unsigned nelements)
|
||||||
|
{
|
||||||
|
if (self)
|
||||||
|
while (self->elements_free < nelements)
|
||||||
|
if (!mpool_cluster_alloc_ (self))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
nobug_mpool_dump (const_MPool self,
|
||||||
|
const int depth,
|
||||||
|
const char* file,
|
||||||
|
const int line,
|
||||||
|
const char* func)
|
||||||
|
{
|
||||||
|
if (self && depth)
|
||||||
|
{
|
||||||
|
DUMP_LOG ("mpool %p: ", self);
|
||||||
|
|
||||||
|
if (depth > 1)
|
||||||
|
{
|
||||||
|
DUMP_LOG (" elements_per_cluster %u: ", self->elements_per_cluster);
|
||||||
|
DUMP_LOG (" elements_free %u: ", self->elements_free);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (depth > 2)
|
||||||
|
{
|
||||||
|
DUMP_LOG (" clusters %p: ", self->clusters);
|
||||||
|
int i = 0;
|
||||||
|
LLIST_FOREACH (&self->clusters, cluster)
|
||||||
|
DUMP_LOG (" %p: %u", cluster, ++i);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (depth > 3)
|
||||||
|
{
|
||||||
|
DUMP_LOG (" freelist %p: ", self->freelist);
|
||||||
|
int i = 0;
|
||||||
|
LLIST_FOREACH (&self->freelist, node)
|
||||||
|
DUMP_LOG (" %p: %u", node, ++i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
173
src/lib/mpool.h
Normal file
173
src/lib/mpool.h
Normal file
|
|
@ -0,0 +1,173 @@
|
||||||
|
/*
|
||||||
|
mpool.h - memory pool for constant sized objects
|
||||||
|
|
||||||
|
Copyright (C) Lumiera.org
|
||||||
|
2009, Christian Thaeter <ct@pipapo.org>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <nobug.h>
|
||||||
|
#include "lib/llist.h"
|
||||||
|
#include "include/logging.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
//mpool Memory Pools
|
||||||
|
//mpool ------------
|
||||||
|
//mpool
|
||||||
|
//mpool This memory pools are implemented as clusters of fixed sized elements. New clusters
|
||||||
|
//mpool are allocated on demand or manually preallocated with a `reserve()` operation. The pool
|
||||||
|
//mpool can be optimized by a `optimize()` operation which sorts the freelist by the addresses of
|
||||||
|
//mpool free elements and optionally moves elements (when a move function is provided) to lower
|
||||||
|
//mpool to fill up holes and improve cache locality.
|
||||||
|
//mpool Clusters can be reclaimed with a `collect()` operation. Optimization and Collection are
|
||||||
|
//mpool optional and are never automatically called.
|
||||||
|
//mpool
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
//index.mpool_destroy_fn xref:mpool_destroy_fn[mpool_destroy_fn]:: function prototype for destroying elements
|
||||||
|
//mpool [[mpool_destroy_fn]]
|
||||||
|
//mpool .mpool_destroy_fn
|
||||||
|
//mpool When a memory pool gets destroyed it can call a destructor for any element which is still in the pool.
|
||||||
|
//mpool This destructor is optional.
|
||||||
|
//mpool
|
||||||
|
//mpool typedef void (*mpool_destroy_fn)(void* self)
|
||||||
|
//mpool
|
||||||
|
//mpool `self`::
|
||||||
|
//mpool element to be destroyed
|
||||||
|
//mpool
|
||||||
|
*/
|
||||||
|
typedef void (*mpool_destroy_fn)(void* self);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
//index.struct_mpool xref:struct_mpool[mpool (struct)]:: the memory pool management structure
|
||||||
|
//mpool [[struct_mpool]]
|
||||||
|
//mpool .mpool
|
||||||
|
//mpool typedef struct mpool_struct mpool
|
||||||
|
//mpool typedef mpool* MPool
|
||||||
|
//mpool typedef const mpool* const_MPool
|
||||||
|
//mpool
|
||||||
|
//mpool This structure should be considered opaque except for the documented members
|
||||||
|
//mpool which may be read.
|
||||||
|
//mpool
|
||||||
|
//mpool `unsigned mpool.elements_free`::
|
||||||
|
//mpool number of free elements in the pool
|
||||||
|
//mpool
|
||||||
|
*/
|
||||||
|
typedef struct mpool_struct mpool;
|
||||||
|
typedef mpool* MPool;
|
||||||
|
typedef const mpool* const_MPool;
|
||||||
|
|
||||||
|
struct mpool_struct
|
||||||
|
{
|
||||||
|
llist freelist;
|
||||||
|
llist clusters;
|
||||||
|
size_t elem_size;
|
||||||
|
unsigned elements_per_cluster;
|
||||||
|
unsigned elements_free; /* a counter of free elements is the price we pay to support a reserve() operation */
|
||||||
|
void* locality;
|
||||||
|
mpool_destroy_fn destroy;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
//index.mpool_init xref:mpool_init[mpool_init]:: initialize a new memory pool
|
||||||
|
//mpool [[mpool_init]]
|
||||||
|
//mpool .mpool_init
|
||||||
|
//mpool Memory pools must be initialized before being used.
|
||||||
|
//mpool
|
||||||
|
//mpool MPool mpool_init (MPool self, size_t elem_size, unsigned elements_per_cluster, mpool_move_fn mv, mpool_destroy_fn dtor)
|
||||||
|
//mpool
|
||||||
|
//mpool `self`::
|
||||||
|
//mpool pointer to the memory pool structure to be initialized
|
||||||
|
//mpool `elem_size`::
|
||||||
|
//mpool size for a single element
|
||||||
|
//mpool `elements_per_cluster`::
|
||||||
|
//mpool how many elements to put into a cluster
|
||||||
|
//mpool `dtor`::
|
||||||
|
//mpool pointer to an optional destructor function or NULL
|
||||||
|
//mpool return::
|
||||||
|
//mpool self
|
||||||
|
//mpool
|
||||||
|
*/
|
||||||
|
MPool
|
||||||
|
mpool_init (MPool self, size_t elem_size, unsigned elements_per_cluster, mpool_destroy_fn dtor);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
//index.mpool_destroy xref:mpool_destroy[mpool_destroy]:: destroy a memory pool
|
||||||
|
//mpool [[mpool_destroy]]
|
||||||
|
//mpool .mpool_destroy
|
||||||
|
//mpool A memory pool is not used anymore it should be destroyed. This frees all memory allocated with it.
|
||||||
|
//mpool When a destructor was provided at construction time, then this destructor is used on all non free elements
|
||||||
|
//mpool before before the memory is freed. If no destructor was given then the memory is just freed.
|
||||||
|
//mpool
|
||||||
|
//mpool MPool mpool_destroy (MPool self)
|
||||||
|
//mpool
|
||||||
|
//mpool `self`::
|
||||||
|
//mpool pointer to an initialized memory pool to be destroyed.
|
||||||
|
//mpool return::
|
||||||
|
//mpool self
|
||||||
|
//mpool
|
||||||
|
//mpool
|
||||||
|
*/
|
||||||
|
MPool
|
||||||
|
mpool_destroy (MPool self);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
query how much free elements are available
|
||||||
|
*/
|
||||||
|
unsigned
|
||||||
|
mpool_available (MPool self);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
resize the pool that at least nelements become available without cluster reallocations
|
||||||
|
*/
|
||||||
|
MPool
|
||||||
|
mpool_reserve (MPool self, unsigned nelements);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
allocate and initialize a new cluster (internal)
|
||||||
|
*/
|
||||||
|
MPool
|
||||||
|
mpool_cluster_alloc_ (MPool self);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
alloc one element from the pool
|
||||||
|
*/
|
||||||
|
void* mpool_alloc (MPool self);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
put a element back on the pool
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
mpool_free (MPool self, void* element);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
nobug_mpool_dump (const_MPool self,
|
||||||
|
const int depth,
|
||||||
|
const char* file,
|
||||||
|
const int line,
|
||||||
|
const char* func);
|
||||||
46
tests/15mpool.tests
Normal file
46
tests/15mpool.tests
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
TESTING "Memory pool tests" ./test-mpool
|
||||||
|
|
||||||
|
TEST "init/destroy" basic <<END
|
||||||
|
err: initialized
|
||||||
|
err: allocated
|
||||||
|
err: freed
|
||||||
|
err: destroyed
|
||||||
|
return: 0
|
||||||
|
END
|
||||||
|
|
||||||
|
|
||||||
|
TEST "auto destruction" destroy <<END
|
||||||
|
return: !0
|
||||||
|
END
|
||||||
|
|
||||||
|
|
||||||
|
TEST "cluster allocation" clusters <<END
|
||||||
|
return: !0
|
||||||
|
END
|
||||||
|
|
||||||
|
|
||||||
|
TEST "random usage" random <<END
|
||||||
|
return: !0
|
||||||
|
END
|
||||||
|
|
||||||
|
|
||||||
|
TEST "stats" statscheck <<END
|
||||||
|
return: !0
|
||||||
|
END
|
||||||
|
|
||||||
|
|
||||||
|
TEST "optimizer" optimize <<END
|
||||||
|
return: !0
|
||||||
|
END
|
||||||
|
|
||||||
|
|
||||||
|
TEST "collector" collect <<END
|
||||||
|
return: !0
|
||||||
|
END
|
||||||
|
|
||||||
|
|
||||||
|
TEST "reserve" reserve <<END
|
||||||
|
return: !0
|
||||||
|
END
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -34,6 +34,11 @@ test_llist_SOURCES = $(tests_srcdir)/library/test-llist.c
|
||||||
test_llist_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror
|
test_llist_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror
|
||||||
test_llist_LDADD = $(NOBUGMT_LUMIERA_LIBS) liblumiera.la -lboost_program_options-mt -lboost_regex-mt
|
test_llist_LDADD = $(NOBUGMT_LUMIERA_LIBS) liblumiera.la -lboost_program_options-mt -lboost_regex-mt
|
||||||
|
|
||||||
|
check_PROGRAMS += test-mpool
|
||||||
|
test_mpool_SOURCES = $(tests_srcdir)/library/test-mpool.c
|
||||||
|
test_mpool_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror
|
||||||
|
test_mpool_LDADD = liblumiera.la $(NOBUGMT_LUMIERA_LIBS) liblumieracommon.la liblumieraproc.la -ldl -lboost_program_options-mt -lboost_regex-mt
|
||||||
|
|
||||||
check_PROGRAMS += test-psplay
|
check_PROGRAMS += test-psplay
|
||||||
test_psplay_SOURCES = $(tests_srcdir)/library/test-psplay.c
|
test_psplay_SOURCES = $(tests_srcdir)/library/test-psplay.c
|
||||||
test_psplay_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror -I$(top_srcdir)/src/
|
test_psplay_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror -I$(top_srcdir)/src/
|
||||||
|
|
|
||||||
191
tests/library/test-mpool.c
Normal file
191
tests/library/test-mpool.c
Normal file
|
|
@ -0,0 +1,191 @@
|
||||||
|
/*
|
||||||
|
test-mpool.c - memory pool for constant sized objects
|
||||||
|
|
||||||
|
Copyright (C) Lumiera.org
|
||||||
|
2009, Christian Thaeter <ct@pipapo.org>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "tests/test.h"
|
||||||
|
#include "lib/mpool.h"
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
dtor (void* o)
|
||||||
|
{
|
||||||
|
ECHO("%x @%p", *(int*)o, o);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
move (void* a, void* b)
|
||||||
|
{
|
||||||
|
ECHO ("%p (%x) to %p ", b, *(int*)b, a);
|
||||||
|
*(int*)a = *(int*)b;
|
||||||
|
*(void**)b = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
TESTS_BEGIN
|
||||||
|
|
||||||
|
TEST ("basic")
|
||||||
|
{
|
||||||
|
mpool mypool;
|
||||||
|
mpool_init (&mypool, sizeof(void*), 10, move, dtor);
|
||||||
|
ECHO ("initialized");
|
||||||
|
|
||||||
|
void* element;
|
||||||
|
element = mpool_alloc (&mypool);
|
||||||
|
ECHO ("allocated %p", element);
|
||||||
|
*(int*)element = 0xdeadbabe;
|
||||||
|
|
||||||
|
DUMP(NOBUG_ON, mpool, &mypool, 4);
|
||||||
|
|
||||||
|
mpool_free (&mypool, element);
|
||||||
|
ECHO ("freed");
|
||||||
|
|
||||||
|
DUMP(NOBUG_ON, mpool, &mypool, 4);
|
||||||
|
|
||||||
|
mpool_destroy (&mypool);
|
||||||
|
ECHO ("destroyed");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST ("destroy")
|
||||||
|
{
|
||||||
|
mpool mypool;
|
||||||
|
mpool_init (&mypool, sizeof(void*), 10, move, dtor);
|
||||||
|
ECHO ("initialized");
|
||||||
|
|
||||||
|
void* element;
|
||||||
|
element = mpool_alloc (&mypool);
|
||||||
|
ECHO ("allocated %p", element);
|
||||||
|
*(int*)element = 0xbabeface;
|
||||||
|
|
||||||
|
DUMP(NOBUG_ON, mpool, &mypool, 4);
|
||||||
|
|
||||||
|
mpool_destroy (&mypool);
|
||||||
|
ECHO ("destroyed");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST ("clusters")
|
||||||
|
{
|
||||||
|
mpool mypool;
|
||||||
|
mpool_init (&mypool, sizeof(void*), 2, move, dtor);
|
||||||
|
ECHO ("initialized");
|
||||||
|
|
||||||
|
for (int i = 1; i <= 5; ++i)
|
||||||
|
{
|
||||||
|
void* element;
|
||||||
|
element = mpool_alloc (&mypool);
|
||||||
|
ECHO ("allocated %p", element);
|
||||||
|
*(int*)element = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
DUMP(NOBUG_ON, mpool, &mypool, 4);
|
||||||
|
|
||||||
|
mpool_destroy (&mypool);
|
||||||
|
ECHO ("destroyed");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST ("clusters_big")
|
||||||
|
{
|
||||||
|
mpool mypool;
|
||||||
|
mpool_init (&mypool, sizeof(void*), 200, move, dtor);
|
||||||
|
ECHO ("initialized");
|
||||||
|
|
||||||
|
for (int i = 1; i <= 700; ++i)
|
||||||
|
{
|
||||||
|
void* element;
|
||||||
|
element = mpool_alloc (&mypool);
|
||||||
|
ECHO ("allocated %p", element);
|
||||||
|
*(int*)element = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
DUMP(NOBUG_ON, mpool, &mypool, 4);
|
||||||
|
|
||||||
|
mpool_destroy (&mypool);
|
||||||
|
ECHO ("destroyed");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST ("collect")
|
||||||
|
{
|
||||||
|
mpool mypool;
|
||||||
|
mpool_init (&mypool, 24, 4, move, dtor);
|
||||||
|
ECHO ("initialized");
|
||||||
|
|
||||||
|
void* elem[32];
|
||||||
|
|
||||||
|
for (int i = 1; i <= 15; ++i)
|
||||||
|
{
|
||||||
|
elem[i] = mpool_alloc (&mypool);
|
||||||
|
*(int*)(elem[i]) = i;
|
||||||
|
}
|
||||||
|
ECHO ("allocated");
|
||||||
|
|
||||||
|
for (int i = 1; i <= 15; i+=3)
|
||||||
|
{
|
||||||
|
mpool_free (&mypool, elem[i]);
|
||||||
|
}
|
||||||
|
ECHO ("freed some");
|
||||||
|
DUMP(NOBUG_ON, mpool, &mypool, 4);
|
||||||
|
|
||||||
|
mpool_collect (&mypool, 0);
|
||||||
|
ECHO ("collected");
|
||||||
|
|
||||||
|
DUMP(NOBUG_ON, mpool, &mypool, 4);
|
||||||
|
|
||||||
|
mpool_destroy (&mypool);
|
||||||
|
ECHO ("destroyed");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST ("collect_no_move")
|
||||||
|
{
|
||||||
|
mpool mypool;
|
||||||
|
mpool_init (&mypool, 24, 4, NULL, dtor);
|
||||||
|
ECHO ("initialized");
|
||||||
|
|
||||||
|
void* elem[32];
|
||||||
|
|
||||||
|
for (int i = 1; i <= 15; ++i)
|
||||||
|
{
|
||||||
|
elem[i] = mpool_alloc (&mypool);
|
||||||
|
*(int*)(elem[i]) = i;
|
||||||
|
}
|
||||||
|
ECHO ("allocated");
|
||||||
|
|
||||||
|
for (int i = 1; i <= 15; i+=3)
|
||||||
|
{
|
||||||
|
mpool_free (&mypool, elem[i]);
|
||||||
|
}
|
||||||
|
ECHO ("freed some");
|
||||||
|
DUMP(NOBUG_ON, mpool, &mypool, 4);
|
||||||
|
|
||||||
|
mpool_collect (&mypool, 0);
|
||||||
|
ECHO ("collected");
|
||||||
|
|
||||||
|
DUMP(NOBUG_ON, mpool, &mypool, 4);
|
||||||
|
|
||||||
|
mpool_destroy (&mypool);
|
||||||
|
ECHO ("destroyed");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST ("reserve")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TESTS_END
|
||||||
Loading…
Reference in a new issue