From 2a9d59ccd03f264515ffe72181b9080fa457095b Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Mon, 11 May 2009 17:08:34 +0200 Subject: [PATCH] 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 --- src/include/logging.h | 1 + src/lib/Makefile.am | 2 + src/lib/mpool.c | 487 +++++++++++++++++++++++++++++++++++++ src/lib/mpool.h | 173 +++++++++++++ tests/15mpool.tests | 46 ++++ tests/Makefile.am | 5 + tests/library/test-mpool.c | 191 +++++++++++++++ 7 files changed, 905 insertions(+) create mode 100644 src/lib/mpool.c create mode 100644 src/lib/mpool.h create mode 100644 tests/15mpool.tests create mode 100644 tests/library/test-mpool.c diff --git a/src/include/logging.h b/src/include/logging.h index 202836c08..4d674bc03 100644 --- a/src/include/logging.h +++ b/src/include/logging.h @@ -89,6 +89,7 @@ NOBUG_CPP_DEFINE_FLAG_PARENT ( proc_dbg, debugging); NOBUG_CPP_DEFINE_FLAG_PARENT ( gui_dbg, debugging); /** base if debug logging for the support library */ 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 ( resourcecollector_dbg, library_dbg); NOBUG_CPP_DEFINE_FLAG_PARENT ( mutex_dbg, library_dbg); diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 8991e2e39..98a0eb91d 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -23,6 +23,7 @@ liblumiera_la_CXXFLAGS = $(AM_CXXFLAGS) -Wall -Wextra liblumiera_la_SOURCES = \ $(liblumiera_la_srcdir)/error.c \ + $(liblumiera_la_srcdir)/mpool.c \ $(liblumiera_la_srcdir)/exception.cpp \ $(liblumiera_la_srcdir)/mutex.c \ $(liblumiera_la_srcdir)/recmutex.c \ @@ -51,6 +52,7 @@ liblumiera_la_SOURCES = \ noinst_HEADERS += \ $(liblumiera_la_srcdir)/error.h \ $(liblumiera_la_srcdir)/error.hpp \ + $(liblumiera_la_srcdir)/mpool.h \ $(liblumiera_la_srcdir)/mutex.h \ $(liblumiera_la_srcdir)/recmutex.h \ $(liblumiera_la_srcdir)/rwlock.h \ diff --git a/src/lib/mpool.c b/src/lib/mpool.c new file mode 100644 index 000000000..0810efacc --- /dev/null +++ b/src/lib/mpool.c @@ -0,0 +1,487 @@ +/* + mpool.h - memory pool for constant sized objects + + Copyright (C) Lumiera.org + 2009, Christian Thaeter + + 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 +#include +#include +#include +#include + +#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<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<>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<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<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); + } + } +} + + diff --git a/src/lib/mpool.h b/src/lib/mpool.h new file mode 100644 index 000000000..509e8098a --- /dev/null +++ b/src/lib/mpool.h @@ -0,0 +1,173 @@ +/* + mpool.h - memory pool for constant sized objects + + Copyright (C) Lumiera.org + 2009, Christian Thaeter + + 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 +#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); diff --git a/tests/15mpool.tests b/tests/15mpool.tests new file mode 100644 index 000000000..97efac290 --- /dev/null +++ b/tests/15mpool.tests @@ -0,0 +1,46 @@ +TESTING "Memory pool tests" ./test-mpool + +TEST "init/destroy" basic < + + 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