diff --git a/README.md b/README.md index dc1f43d4..35aa9a7b 100644 --- a/README.md +++ b/README.md @@ -195,9 +195,6 @@ Finally, headers for compatibility with non C11 compilers: * [m-atomic.h](#m-atomic): header for ensuring compatibility between C's `stdatomic.h` and C++'s atomic header (provide also its own implementation if nothing is available), * [m-thread.h](#m-thread): header for providing a very thin layer across multiple implementation of mutex/threads (C11/PTHREAD/WIN32). -The following headers are obsolete and will be removed in the next major release: -* [m-mempool.h](#m-mempool): header for creating specialized & fast memory allocator, - Each containers define their iterators (if it is meaningful). All containers try to expose the same common interface: @@ -1823,39 +1820,6 @@ int main(void) { } ``` -If the given oplist contain the method `MEMPOOL`, then `LIST_DEF` macro will create -a dedicated mempool that is named with the given value of the method `MEMPOOL`. -This mempool (see [mempool chapter](#m-mempool)) is optimized for this kind of list: - -* it creates a mempool named by the concatenation of `name` and `_mempool`, -* it creates a variable named by the value of the method `MEMPOOL` with the linkage -defined by the value of the method `MEMPOOL_LINKAGE` (can be extern, static or none). -This variable is shared by all lists of the same type. -* it links the memory allocation of the list to use this mempool with this variable. - -`mempool` create heavily efficient list. However, it is only worth the -effort in some heavy performance context. -Using mempool should be therefore avoided except in performance critical code. -The created mempool has to be explicitly initialized before using any -methods of the created list by calling `mempool_list_name_init(variable)` -and cleared by calling `mempool_list_name_clear(variable)`. - -Example: - -```C -LIST_DEF(list_uint, unsigned int, (MEMPOOL(list_mpool), MEMPOOL_LINKAGE(static))) - -static void test_list (size_t n) -{ - list_uint_mempool_init(list_mpool); - M_LET(a1, LIST_OPLIST(uint)) { - for(size_t i = 0; i < n; i++) - list_uint_push_back(a1, rand_get() ); - } - list_uint_mempool_clear(list_mpool); -} -``` - #### `LIST_OPLIST(name [, oplist])` Return the oplist of the list defined by calling `LIST_DEF` @@ -7050,8 +7014,6 @@ M_GET_NEW oplist M_GET_DEL oplist M_GET_REALLOC oplist M_GET_FREE oplist -M_GET_MEMPOOL oplist -M_GET_MEMPOOL_LINKAGE oplist M_GET_HASH oplist M_GET_EQUAL oplist M_GET_CMP oplist @@ -8704,69 +8666,6 @@ The line and filename fields of the exception are filled automatically by the ma _________________ -### M-MEMPOOL - -> [!NOTE] -> This header is obsolete: `M_USE_CONTEXT` should be used instead to pass custom memory context to containers. - -This header is for generating specialized and optimized memory pools: -it will generate specialized functions to allocate and free only one kind of an object. -The mempool functions are not specially thread safe for a given mempool, -but the mempool variable can have the attribute `M_THREAD_ATTR` -so that each thread has its own instance of the mempool. - -The memory pool has to be initialized and cleared like any other variable. -Clearing the memory pool will free all the memory that has been allocated -within this memory pool. - -#### `MEMPOOL_DEF(name, type)` - -Generate specialized functions & types prefixed by `name` to `alloc` and `free` an object of type `type`. - -Example: - -```C -MEMPOOL_DEF(mempool_uint, unsigned int) - -mempool_uint_t m; - -void f(void) { - mempool_uint_init(m); - unsigned int *p = mempool_uint_alloc(m); - *p = 17; - mempool_uint_free(m, p); - mempool_uint_clear(m); -} -``` - -#### Created methods - -The following methods are automatically and properly created by the previous macros. In the following methods, name stands for the name given to the macro that is used to identify the type. - -##### `name_t` - -The type of a mempool. - -##### `void name_init(name_t m)` - -Initialize the mempool `m`. - -##### `void name_clear(name_t m)` - -Clear the mempool `m`. -All allocated objects associated to the this mempool that weren't explicitly freed will be deleted too (without calling their clear method). - -##### `type *name_alloc(name_t m)` - -Create a new object of type `type` and return a new pointer to the uninitialized object. - -##### `void name_free(name_t m, type *p)` - -Free the object `p` created by the call to `name_alloc`. -The clear method of the type is not called. - -_________________ - ### M-SERIAL-JSON This header is for defining an instance of the serial interface diff --git a/example/ex11-small-name.c b/example/ex11-small-name.c index 2a92a3c3..64b1bf6c 100644 --- a/example/ex11-small-name.c +++ b/example/ex11-small-name.c @@ -15,7 +15,6 @@ #include "m-genint.h" #include "m-i-list.h" #include "m-list.h" -#include "m-mempool.h" #include "m-prioqueue.h" #include "m-rbtree.h" #include "m-serial-bin.h" diff --git a/m-core.h b/m-core.h index 53b0fff5..f464fc6e 100644 --- a/m-core.h +++ b/m-core.h @@ -3531,8 +3531,6 @@ M_INLINE size_t m_core_cstr_hash(const char str[]) #define M_X_DEL_DEL(a) ,a, #define M_X_REALLOC_REALLOC(a) ,a, #define M_X_FREE_FREE(a) ,a, -#define M_X_MEMPOOL_MEMPOOL(a) ,a, -#define M_X_MEMPOOL_LINKAGE_MEMPOOL_LINKAGE(a) ,a, #define M_X_SIZE_SIZE(a) ,a, #define M_X_CONTEXT_CONTEXT(a) ,a, #define M_X_POLICY_POLICY(a) ,a, @@ -3624,8 +3622,6 @@ M_INLINE size_t m_core_cstr_hash(const char str[]) #define M_GET_DEL(...) M_GET_METHOD(DEL, M_MEMORY_DEL, __VA_ARGS__) #define M_GET_REALLOC(...) M_GET_METHOD(REALLOC, M_MEMORY_REALLOC, __VA_ARGS__) #define M_GET_FREE(...) M_GET_METHOD(FREE, M_MEMORY_FREE, __VA_ARGS__) -#define M_GET_MEMPOOL(...) M_GET_METHOD(MEMPOOL, M_NO_DEF_MEMPOOL, __VA_ARGS__) -#define M_GET_MEMPOOL_LINKAGE(...) M_GET_METHOD(MEMPOOL_LINKAGE, , __VA_ARGS__) #define M_GET_SIZE(...) M_GET_METHOD(SIZE, 0, __VA_ARGS__) #define M_GET_CONTEXT(...) M_GET_METHOD(CONTEXT, 0, __VA_ARGS__) #define M_GET_POLICY(...) M_GET_METHOD(POLICY, 0, __VA_ARGS__) @@ -3702,8 +3698,6 @@ M_INLINE size_t m_core_cstr_hash(const char str[]) #define M_CALL_DEL(oplist, ...) M_APPLY_API(M_GET_DEL oplist, oplist, m_context, __VA_ARGS__) #define M_CALL_REALLOC(oplist, ...) M_APPLY_API(M_GET_REALLOC oplist, oplist, m_context, __VA_ARGS__) #define M_CALL_FREE(oplist, ...) M_APPLY_API(M_GET_FREE oplist, oplist, m_context, __VA_ARGS__) -#define M_CALL_MEMPOOL(oplist, ...) M_APPLY_API(M_GET_MEMPOOL oplist, oplist, __VA_ARGS__) -#define M_CALL_MEMPOOL_LINKAGE(oplist, ...) M_APPLY_API(M_GET_MEMPOOL_LINKAGE oplist, oplist, __VA_ARGS__) //#define M_CALL_SIZE(oplist, ...) M_APPLY_API(M_GET_SIZE oplist, oplist, __VA_ARGS__) //#define M_CALL_CONTEXT(oplist, ...) M_APPLY_API(M_GET_CONTEXT oplist, oplist, __VA_ARGS__) //#define M_CALL_POLICY(oplist, ...) M_APPLY_API(M_GET_POLICY oplist, oplist, __VA_ARGS__) @@ -3916,7 +3910,6 @@ M_INLINE size_t m_core_cstr_hash(const char str[]) #define M_NO_DEF_IN_SERIAL(...) M_NO_DEFAULT(IN_SERIAL, m_serial_return_code_t) #define M_NO_DEF_OOR_SET(...) M_NO_DEFAULT(OOR_SET, void) #define M_NO_DEF_OOR_EQUAL(...) M_NO_DEFAULT(OOR_EQUAL, bool) -#define M_NO_DEF_MEMPOOL(...) M_NO_DEFAULT(MEMPOOL, void) /* Create a type with an invalid static assertion. Creating a type allowed the macro into something not too bad diff --git a/m-mempool.h b/m-mempool.h deleted file mode 100644 index b7017a73..00000000 --- a/m-mempool.h +++ /dev/null @@ -1,207 +0,0 @@ -/* - * M*LIB - MEMPOOL module - * - * Copyright (c) 2017-2025, Patrick Pelissier - * All rights reserved. - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * + Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * + Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ -#ifndef MSTARLIB_MEMPOOL_H -#define MSTARLIB_MEMPOOL_H - -#include "m-core.h" - -// TODO: Full rework of the mempool module to provide a generic arena allocator -// instead using the new pool context pointer. - -/* Fast, fixed size, thread unsafe allocator based on memory regions. - No oplist is needed. - USAGE: - MEMPOOL_DEF(name, type) - Example: - MEMPOOL_DEF(mempool_uint, unsigned int) - ... - mempool_uint_t m; - mempool_uint_init(m); - unsigned int *ptr = mempool_uint_alloc(m); - *ptr = 17; - mempool_uint_free(m, ptr); - mempool_uint_clear(m); // Give back memory to system -*/ -#define M_MEMPOOL_DEF(name, type) \ - M_MEMPOOL_DEF_AS(name, M_F(name,_t), type) - - -/* Fast, fixed Size, thread unsafe allocator based on memory region. - USAGE: - MEMPOOL_DEF_AS(name, name_t, type) -*/ -#define M_MEMPOOL_DEF_AS(name, name_t, type) \ - M_BEGIN_PROTECTED_CODE \ - M_M3MPOOL_DEF_P2(name, type, name_t ) \ - M_END_PROTECTED_CODE - - -/* User shall be able to customize the size of the region segment and/or - the minimum number of elements. - The default is the number of elements that fits in 16KB, or 256 - is the size of the type is too big. -*/ -#ifndef M_USE_MEMPOOL_MAX_PER_SEGMENT -#define M_USE_MEMPOOL_MAX_PER_SEGMENT(type) \ - M_MAX((16*1024-sizeof(unsigned int) - 2*sizeof(void*)) / sizeof (type), 256U) -#endif - - -/*****************************************************************************/ -/********************************** INTERNAL *********************************/ -/*****************************************************************************/ - -/* - Technically, it uses a list of memory regions, where multiple - allocations are performed in each region. However, it - can not use m-list since it may be expanded from LIST_DEF - (recursive dependency problem). */ -#define M_M3MPOOL_DEF_P2(name, type, name_t) \ - M_M3MPOOL_DEF_TYPE(name, type, name_t) \ - M_M3MPOOL_DEF_CORE(name, type, name_t) - -/* Define the types of the mempool */ -#define M_M3MPOOL_DEF_TYPE(name, type, name_t) \ - \ - /* Define the type of element in a segment of the mempool. \ - Either it is the basic type or a pointer to another one. */ \ - typedef union M_F(name,_union_s) { \ - type t; \ - union M_F(name,_union_s) *next; \ - } M_F(name,_union_ct); \ - \ - /* Define a segment of a mempool. \ - It is an array of basic type, each segment is in a linked list */ \ - typedef struct M_F(name,_segment_s) { \ - unsigned int count; \ - struct M_F(name,_segment_s) *next; \ - M_F(name,_union_ct) tab[M_USE_MEMPOOL_MAX_PER_SEGMENT(type)]; \ - } M_F(name,_segment_ct); \ - \ - /* Define a mempool. \ - It is a pointer to the first free object within the segments \ - and the segments themselves */ \ - typedef struct M_F(name, _s) { \ - M_F(name,_union_ct) *free_list; \ - M_F(name,_segment_ct) *current_segment; \ - } name_t[1]; \ - - -/* Define the core functions of the mempool */ -#define M_M3MPOOL_DEF_CORE(name, type, name_t) \ - \ - M_INLINE void \ - M_F(name,_init)(name_t mem) \ - { \ - mem->free_list = NULL; \ - mem->current_segment = M_MEMORY_ALLOC(m_context, M_F(name,_segment_ct)); \ - if (M_UNLIKELY_NOMEM(mem->current_segment == NULL)) { \ - M_MEMORY_FULL(M_F(name,_segment_ct), 1); \ - } \ - mem->current_segment->next = NULL; \ - mem->current_segment->count = 0; \ - M_M3MPOOL_CONTRACT(mem, type); \ - } \ - \ - M_INLINE void \ - M_F(name,_clear)(name_t mem) \ - { \ - M_M3MPOOL_CONTRACT(mem, type); \ - M_F(name,_segment_ct) *segment = mem->current_segment; \ - while (segment != NULL) { \ - M_F(name,_segment_ct) *next = segment->next; \ - M_MEMORY_DEL (m_context, segment); \ - segment = next; \ - } \ - /* Clean pointers to be safer */ \ - mem->free_list = NULL; \ - mem->current_segment = NULL; \ - } \ - \ - M_INLINE type * \ - M_F(name,_alloc)(name_t mem) \ - { \ - M_M3MPOOL_CONTRACT(mem, type); \ - /* Test if one object is in the free list */ \ - M_F(name,_union_ct) *ret = mem->free_list; \ - if (ret != NULL) { \ - /* Yes, so return it, and pop it from the free list */ \ - mem->free_list = ret->next; \ - return &ret->t; \ - } \ - /* No cheap free object exist. Test within a segment */ \ - M_F(name,_segment_ct) *segment = mem->current_segment; \ - M_ASSERT(segment != NULL); \ - unsigned int count = segment->count; \ - /* If segment is full, allocate a new one from the system */ \ - if (M_UNLIKELY (count >= M_USE_MEMPOOL_MAX_PER_SEGMENT(type))) { \ - M_F(name,_segment_ct) *new_segment = M_MEMORY_ALLOC (m_context, M_F(name,_segment_ct)); \ - if (M_UNLIKELY_NOMEM (new_segment == NULL)) { \ - M_MEMORY_FULL(M_F(name,_segment_ct), 1); \ - } \ - new_segment->next = segment; \ - new_segment->count = 0; \ - mem->current_segment = new_segment; \ - segment = new_segment; \ - count = 0; \ - } \ - /* Return the object as the last element of the current segment */ \ - ret = &segment->tab[count]; \ - segment->count = count + 1; \ - M_M3MPOOL_CONTRACT(mem, type); \ - return &ret->t; \ - } \ - \ - M_INLINE void \ - M_F(name,_free)(name_t mem, type *ptr) \ - { \ - M_M3MPOOL_CONTRACT(mem, type); \ - /* NOTE: Unsafe cast: suppose that the given pointer \ - was allocated by the previous alloc function. */ \ - M_F(name,_union_ct) *ret = (M_F(name,_union_ct) *)(uintptr_t)ptr; \ - /* Add the object back in the free list */ \ - ret->next = mem->free_list; \ - mem->free_list = ret; \ - /* NOTE: the objects are NOT given back to the system until the mempool \ - is fully cleared */ \ - M_M3MPOOL_CONTRACT(mem, type); \ - } \ - -/* MEMPOOL contract. We only control the current segment. */ -#define M_M3MPOOL_CONTRACT(mempool, type) do { \ - M_ASSERT((mempool) != NULL); \ - M_ASSERT((mempool)->current_segment != NULL); \ - M_ASSERT((mempool)->current_segment->count <= M_USE_MEMPOOL_MAX_PER_SEGMENT(type)); \ - } while (0) - - -/********************************** INTERNAL *********************************/ - -#if M_USE_SMALL_NAME -#define MEMPOOL_DEF M_MEMPOOL_DEF -#define MEMPOOL_DEF_AS M_MEMPOOL_DEF_AS -#endif - -#endif diff --git a/tests/test-mmempool.c b/tests/test-mmempool.c deleted file mode 100644 index b9513861..00000000 --- a/tests/test-mmempool.c +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2017-2025, Patrick Pelissier - * All rights reserved. - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * + Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * + Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include "test-obj.h" -#include "m-mempool.h" -#include "coverage.h" - -START_COVERAGE -MEMPOOL_DEF(mempool_uint, unsigned int) -END_COVERAGE - -MEMPOOL_DEF_AS(MempoolDouble, MempoolDouble, unsigned int) - -static void test(void) -{ - mempool_uint_t m; - mempool_uint_init(m); - - unsigned int *tab[100000]; - for(unsigned int i = 0; i < 100000; i++) { - tab[i] = mempool_uint_alloc(m); - *tab[i] = i; - } - for(unsigned int i = 0; i < 100000; i++) { - assert (*tab[i] == i); - } - for(unsigned int i = 0; i < 100000; i+=2) { - mempool_uint_free(m, tab[i]); - tab[i] = NULL; - } - for(unsigned int i = 1; i < 100000; i+=2) { - assert (*tab[i] == i); - } - for(unsigned int i = 0; i < 100000; i+=2) { - tab[i] = mempool_uint_alloc(m); - *tab[i] = i; - } - for(unsigned int i = 0; i < 100000; i++) { - assert (*tab[i] == i); - } - for(unsigned int i = 0; i < 100000; i++) { - mempool_uint_free(m, tab[i]); - tab[i] = NULL; - } - mempool_uint_clear(m); -} - -static void test_double(void) -{ - MempoolDouble m; - MempoolDouble_init(m); - - unsigned int *tab[100000]; - for(unsigned int i = 0; i < 100000; i++) { - tab[i] = MempoolDouble_alloc(m); - *tab[i] = i; - } - for(unsigned int i = 0; i < 100000; i++) { - assert (*tab[i] == i); - } - for(unsigned int i = 0; i < 100000; i+=2) { - MempoolDouble_free(m, tab[i]); - tab[i] = NULL; - } - for(unsigned int i = 1; i < 100000; i+=2) { - assert (*tab[i] == i); - } - for(unsigned int i = 0; i < 100000; i+=2) { - tab[i] = MempoolDouble_alloc(m); - *tab[i] = i; - } - for(unsigned int i = 0; i < 100000; i++) { - assert (*tab[i] == i); - } - for(unsigned int i = 0; i < 100000; i++) { - MempoolDouble_free(m, tab[i]); - tab[i] = NULL; - } - MempoolDouble_clear(m); -} - -int main(void) -{ - test(); - test_double(); - exit(0); -}