/* 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. */ /** @file mpool.h ** Pooled memory allocator for constant sized objects ** *Memory Pools* are implemented as clusters of fixed sized elements. New clusters ** are allocated on demand or manually preallocated with a mpool_reserve() operation. ** Some efforts are taken to ensure (cache) locality of the provided memory. ** All functions are reentrant but not threadsafe, if this is desired it is advised to ** care for proper locking elsewhere. ** @warning as of 2016, this is a stalled development effort towards a pooling allocator. ** An initial working draft was created in 2009, but never challenged by any ** widespread use beyond some test code. We acknowledge that there _will be_ ** some kind of optimised allocator -- yet for the time being we rely on ** several preliminary front-ends, which _could be attached_ to such an ** allocator eventually, but use plain flat heap allocations right now. */ #include #include #include "lib/llist.h" #include "include/logging.h" /** * Function prototype for destroying elements. * When a memory pool gets destroyed it can call a destructor for any element which is still in the pool. * Use of such a destructor is optional. * @param self element to be destroyed */ typedef void (*mpool_destroy_fn)(void* self); /** * memory pool management structure. * This structure should be considered opaque. */ 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; uintptr_t cluster_size; unsigned elements_free; /* a counter of free elements is the price we pay to support a reserve() operation */ void* locality; mpool_destroy_fn destroy; void *(*malloc_hook)(size_t); void (*free_hook)(void *); void* udata; /* free to use by the user, resourcecollector stuff in lumiera*/ }; extern void *(*mpool_malloc_hook)(size_t size); extern void (*mpool_free_hook)(void *ptr); /** called after a mpool got initialised */ extern void (*mpool_init_hook) (MPool self); /** called before a mpool gets destroyed */ extern void (*mpool_destroy_hook) (MPool self); /** * Initialise a new memory pool. * Memory pools must be initialised before being used. One can supply * an optional destructor function for elements, this will be used to destroy elements which are still * in the pool when it gets destroyed itself. The destructor is _NOT_ called when elements are freed. * @param self pointer to the memory pool structure to be initialised * @param elem_size size for a single element * @param elements_per_cluster how many elements to put into a cluster * @param dtor pointer to an optional destructor function, may be `NULL` * @return `self` pointer to the initialised object */ MPool mpool_init (MPool self, size_t elem_size, unsigned elements_per_cluster, mpool_destroy_fn dtor); /** * destroy a memory pool. * A memory pool not used anymore should be destroyed. This frees all memory allocated with it. * When a destructor was provided at construction time, then this destructor is used on all non free elements * before before the clusters are freed. If no destructor was given then the clusters are just freed. * The destroyed memory pool behaves as if it was freshly initialised and can be used again, this is * some kind of exceptional behaviour. * @param self pointer to an initialised memory pool to be destroyed. * @return `self` */ MPool mpool_destroy (MPool self); /** * free unused clusters. */ MPool mpool_purge (MPool self); /** * query number of free elements. * @param self pointer to the memory pool to be queried * @return number of elements available in the pool _without allocating a new cluster_ */ static inline unsigned mpool_available (MPool self) { return self->elements_free; } /** * preallocate elements. * Resize the pool that at least the given number of elements are available without cluster reallocations. * @param self pointer to the memory pool * @param nelements minimum number of elements to preallocate * @return self on success or `NULL` on error */ MPool mpool_reserve (MPool self, unsigned nelements); /** * allocate one element from a MPool. To improve cache locality allocations * are grouped close together to recent allocations. * @param self pointer to the memory pool * @return pointer to the allocated memory on success or `NULL` on error * @note will never fail when enough space was preallocated */ void* mpool_alloc (MPool self); /** * allocate one element close to the given reference element. * To improve cache locality the allocation tries to get an element close by. * @param self pointer to the memory pool * @param near reference to another element which should be close to the returned element _(hint only)_ * @return pointer to the allocated memory on success or `NULL` on error * @note will never fail when enough space was preallocated */ void* mpool_alloc_near (MPool self, void* near); /** * free one element. * Frees the given element and puts it back into the pool for further allocations. * @param self pointer to the memory pool * @param element element to be freed */ void mpool_free (MPool self, void* element); /** diagnostic dump of MPool allocation */ void nobug_mpool_dump (const_MPool self, const int depth, const struct nobug_context dump_context, void* extra);