LUMIERA.clone/src/lib/mpool.c
Ichthyostega 41ad41d1f1 clean-up: sourcefile layout and spell checking
Uniform sequence at start of source files
- copyright claim
- license
- file comment
- header guard
- lumiera includes
- library / system includes

Lumiera uses Brittish spelling. Add an according note to the styleguide.
2014-10-23 23:04:35 +02:00

547 lines
14 KiB
C

/*
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 <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <inttypes.h>
#include <limits.h>
#include "mpool.h"
#if UINTPTR_MAX > 4294967295U /* 64 bit */
#define MPOOL_DIV_SHIFT 6
#define MPOOL_C(c) c ## ULL
#else /* 32 bit */
#define MPOOL_DIV_SHIFT 5
#define MPOOL_C(c) c ## UL
#endif
/*
defaults for the hooks, used when creating mpools
*/
void *(*mpool_malloc_hook)(size_t size) = malloc;
void (*mpool_free_hook)(void *ptr) = free;
/** called after a mpool got initialised */
void (*mpool_init_hook) (MPool self) = NULL;
/** called before a mpool gets destroyed */
void (*mpool_destroy_hook) (MPool self) = NULL;
/*
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 */
char data[]; /* bitmap and elements */
};
typedef struct mpoolnode_struct mpoolnode;
typedef mpoolnode* MPoolnode;
typedef const mpoolnode* const_MPoolnode;
struct mpoolnode_struct
{
llist node;
};
MPool
mpool_cluster_alloc_ (MPool 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))
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->cluster_size = sizeof (mpoolcluster) + /* header */
MPOOL_BITMAP_SIZE (self->elements_per_cluster) + /* bitmap */
self->elem_size * self->elements_per_cluster; /* elements */
self->elements_free = 0;
self->destroy = dtor;
self->locality = NULL;
self->malloc_hook = mpool_malloc_hook;
self->free_hook = mpool_free_hook;
if (mpool_init_hook)
mpool_init_hook (self);
}
return self;
}
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);
uintptr_t quot = index>>MPOOL_DIV_SHIFT;
uintptr_t rem = index & ~((~MPOOL_C(0))<<MPOOL_DIV_SHIFT);
uintptr_t* bitmap = (uintptr_t*)&cluster->data;
return bitmap[quot] & ((uintptr_t)1<<rem);
}
MPool
mpool_destroy (MPool self)
{
TRACE (mpool_dbg, "%p", self);
if (self)
{
if (mpool_destroy_hook)
mpool_destroy_hook (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);
self->free_hook (cluster);
}
llist_init (&self->freelist);
self->elements_free = 0;
self->locality = NULL;
}
return self;
}
MPool
mpool_purge (MPool self)
{
// not UNIMPLEMENTED because valid no-op
PLANNED("To be implemented");
return self;
}
MPool
mpool_cluster_alloc_ (MPool self)
{
MPoolcluster cluster = self->malloc_hook (self->cluster_size);
TRACE (mpool_dbg, "%p", cluster);
if (!cluster)
return NULL;
/* clear the bitmap */
memset (&cluster->data, 0, MPOOL_BITMAP_SIZE (self->elements_per_cluster));
/* initialise 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* cluster_size)
{
if (element < cluster)
return -1;
if ((void*)element > (void*)cluster + (uintptr_t)cluster_size)
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, (void*)self->cluster_size);
}
static inline unsigned
uintptr_nearestbit (uintptr_t v, unsigned n)
{
unsigned r = 0;
uintptr_t mask = MPOOL_C(1)<<n;
while (1)
{
if (v&mask)
{
if (v&mask& ~(~0ULL<<n))
return n-r;
else
return n+r;
}
if (mask == ~MPOOL_C(0))
return ~0U;
++r;
mask |= ((mask<<1)|(mask>>1));
}
}
static inline void*
alloc_near (MPoolcluster cluster, MPool self, void* locality)
{
TRACE (mpool_dbg, "locality %p", locality);
void* begin_of_elements =
(void*)cluster +
sizeof (*cluster) + /* header */
MPOOL_BITMAP_SIZE (((MPool)self)->elements_per_cluster); /* bitmap */
uintptr_t index = (locality - begin_of_elements) / self->elem_size;
uintptr_t quot = index>>MPOOL_DIV_SHIFT;
uintptr_t rem = index & ~((~MPOOL_C(0))<<MPOOL_DIV_SHIFT);
uintptr_t* bitmap = (uintptr_t*)&cluster->data;
unsigned r = ~0U;
/* the bitmap word at locality */
if (bitmap[quot] < UINTPTR_MAX)
{
r = uintptr_nearestbit (~bitmap[quot], rem);
}
/* the bitmap word before locality, this gives a slight bias towards the begin, keeping the pool compact */
else if (quot && bitmap[quot-1] < UINTPTR_MAX)
{
--quot;
r = uintptr_nearestbit (~bitmap[quot], sizeof(uintptr_t)*CHAR_BIT-1);
}
if (r != ~0U && (quot*sizeof(uintptr_t)*CHAR_BIT+r) < self->elements_per_cluster)
{
void* ret = begin_of_elements + ((uintptr_t)(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 */
uintptr_t index = (element - begin_of_elements) / self->elem_size;
uintptr_t quot = index>>MPOOL_DIV_SHIFT;
uintptr_t rem = index & ~((~MPOOL_C(0))<<MPOOL_DIV_SHIFT);
uintptr_t* bitmap = (uintptr_t*)&cluster->data;
bitmap[quot] |= ((uintptr_t)1<<rem);
TRACE (mpool_dbg, "set bit %"PRIuPTR", index %"PRIuPTR", of %p is %"PRIuPTR, rem, quot, element, bitmap[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 */
uintptr_t index = (element - begin_of_elements) / self->elem_size;
uintptr_t quot = index>>MPOOL_DIV_SHIFT;
uintptr_t rem = index & ~((~MPOOL_C(0))<<MPOOL_DIV_SHIFT);
uintptr_t* bitmap = (uintptr_t*)&cluster->data;
bitmap[quot] &= ~((uintptr_t)1<<rem);
TRACE (mpool_dbg, "cleared bit %"PRIuPTR", index %"PRIuPTR", of %p is %"PRIuPTR, rem, quot, element, bitmap[quot]);
}
void*
mpool_alloc (MPool self)
{
TRACE (mpool_dbg);
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)
{
bitmap_set_element (element_cluster_get (self, ret), self, ret);
llist_unlink_fast_ ((LList)ret);
}
self->locality = ret;
--self->elements_free;
return ret;
}
void*
mpool_alloc_near (MPool self, void* near)
{
TRACE (mpool_dbg);
if (!self->elements_free)
{
if (mpool_cluster_alloc_ (self))
{
near = NULL; /* supress alloc_near() */
}
else
{
ERROR (mpool_dbg, "allocation failure");
return NULL;
}
}
void* ret = NULL;
if (near)
{
ret = alloc_near (element_cluster_get (self, near), self, near);
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)
{
bitmap_set_element (element_cluster_get (self, ret), self, ret);
llist_unlink_fast_ ((LList)ret);
}
--self->elements_free;
return ret;
}
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;
uintptr_t quot = index>>MPOOL_DIV_SHIFT;
uintptr_t rem = index & ~((~MPOOL_C(0))<<MPOOL_DIV_SHIFT);
uintptr_t* bitmap = (uintptr_t*)&cluster->data;
unsigned r = ~0U;
/* the bitmap word at locality */
if (bitmap[quot] < UINTPTR_MAX)
{
r = uintptr_nearestbit (~bitmap[quot], 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[quot+1] < UINTPTR_MAX)
{
++quot;
r = uintptr_nearestbit (~bitmap[quot], 0);
}
/* finally the bitmap word before element */
else if (index > 0 && bitmap[quot-1] < UINTPTR_MAX)
{
--quot;
r = uintptr_nearestbit (~bitmap[quot], sizeof(uintptr_t)*CHAR_BIT-1);
}
if (r != ~0U && (quot*sizeof(uintptr_t)*CHAR_BIT+r) < self->elements_per_cluster)
return begin_of_elements + ((uintptr_t)(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);
MPoolnode near = find_near (cluster, self, element);
bitmap_clear_element (cluster, self, element);
llist_init (&((MPoolnode)element)->node);
if (near)
{
TRACE (mpool_dbg, "found near %p", 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);
++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 struct nobug_context dump_context,
void* extra)
{
(void) extra;
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);
}
}
}
/*
// Local Variables:
// mode: C
// c-file-style: "gnu"
// indent-tabs-mode: nil
// End:
*/