diff --git a/README.md b/README.md index 14c0be93..7fd56d22 100644 --- a/README.md +++ b/README.md @@ -1041,18 +1041,37 @@ In which case, this enables you to close the file even if an exception is thrown Memory Allocation functions can be globally set by overriding the following macros before using the definition `_DEF` macros: -* `M_MEMORY_ALLOC (type)` --> `ptr`: return a pointer to a new object of type `type`. -* `M_MEMORY_DEL (ptr)`: free the single object pointed by `ptr`. -* `M_MEMORY_REALLOC (type, ptr, old_number, new_number)` --> `ptr`: return a pointer to an array of `new_number` objects of type `type`, reusing the old array pointed by `ptr` of `old_number` objects. `ptr` can be NULL, in which case the array will be created. -* `M_MEMORY_FREE (type, ptr, number)`: free the array of `number` objects of type `type` pointed by `ptr`. +* `M_MEMORY_ALLOC (context, type)` --> `ptr`: return a pointer to a new object of type `type`. +* `M_MEMORY_DEL (context, ptr)`: free the single object pointed by `ptr`. +* `M_MEMORY_REALLOC (context, type, ptr, old_number, new_number)` --> `ptr`: return a pointer to an array of `new_number` objects of type `type`, reusing the old array pointed by `ptr` of `old_number` objects. `ptr` can be NULL, in which case the array will be created. +* `M_MEMORY_FREE (context, type, ptr, number)`: free the array of `number` objects of type `type` pointed by `ptr`. -`ALLOC` and `DEL` operators are supposed to allocate fixed size single element object (no array). +> [!NOTE] +> You can use `sizeof (type)` and `alignof (type)` to get the size and the alignement of the object. + +> [!NOTE] +> For `M_MEMORY_DEL`, `sizeof *ptr` always identifies the size of the object. + +`ALLOC` and `DEL` operators are supposed to allocate **fixed** size single element object (no array). These objects are not expected to grow. `REALLOC` and `FREE` operators deal with allocated memory for growing objects. Do not mix pointers between both: a pointer allocated by `ALLOC` (resp. `REALLOC`) is supposed to be only freed by `DEL` (resp. `FREE`). There are separated 'free' operators to enable specialization in the allocator (a good allocator can take this hint into account). +`context` is a local user context used to identify the memory context to use for the allocation. +This parameter **only** exists if the library is compiled with the `M_USE_CONTEXT` customization parameter enabled globally. +Otherwise this parameter identifies a non existing symbol `m_context`. + +This parameter shall be discarded if `M_USE_CONTEXT` is not defined (it shall not exist anymore after preprocessing step). +For that, you can define the macros to ignore the first parameter +or you can use GAIA to adapt the call to what your function expects. + +> [!NOTE] +> If you get error on `m_context` being undefined +> it is likely because one of your memory is trying to evaluate the memory context +> without defining `M_USE_CONTEXT`. + `M_MEMORY_ALLOC` and `M_MEMORY_REALLOC` are supposed to return NULL in case of memory allocation failure. The defaults are `malloc`, `free`, `realloc` and `free`. @@ -4724,7 +4743,7 @@ void f(void) { shared_data_push(p, 23); shared_data_push(p, 32); // And send it through 2 channels of communication - comm_buffer_push(comm1, p); // shared_data_acquire is done by the buffer to acquite ownership of the data + comm_buffer_push(comm1, p); // shared_data_acquire is done by the buffer to acquit ownership of the data comm_buffer_push(comm2, p); // Release our ownership of the data shared_data_clear(p); @@ -4909,7 +4928,7 @@ This method is created only if the `PUSH` operator is defined. ##### `bool name_try_push_move(name_t *a, sub_type *el)` Try to push in `*a` the element `*el`,if the container is not full and clear `*el` (return true in this case). -Return false otherwise (cannot push element) and `*el` is still initializated. +Return false otherwise (cannot push element) and `*el` is still initialized. This method is created only if the `PUSH_MOVE` operator is defined. ##### `bool name_try_emplace(name_t *a[, args])` @@ -7967,22 +7986,28 @@ void struct_init_set(struct_t d, struct_t s) All these macro can be overridden before including the header [m-core.h](#m-core) so that they can be adapted to a particular memory pool. -##### `type *M_MEMORY_ALLOC (type)` +##### `type *M_MEMORY_ALLOC (context, type)` Return a pointer to a new allocated non-initialized object of type `type`. In case of allocation error, it returns NULL. +`context` parameter is the memory context parameter as provided by the user. +It only exists if the library is built with `M_USE_CONTEXT` + The default used function is the `malloc` function of the LIBC. -##### `void M_MEMORY_DEL (type *ptr)` +##### `void M_MEMORY_DEL (context, type *ptr)` Delete the cleared object pointed by the pointer `ptr` that was previously allocated by the macro `M_MEMORY_ALLOC`. `ptr` can not be NULL. +`context` parameter is the memory context parameter as provided by the user. +It only exists if the library is built with `M_USE_CONTEXT` + The default used function is the `free` function of the LIBC. -##### `type *M_MEMORY_REALLOC (type, ptr, old_number, new_number)` +##### `type *M_MEMORY_REALLOC (context, type, ptr, old_number, new_number)` Return a pointer to an array of `new_number` objects of type `type` `ptr` is either NULL (in which the array is allocated), @@ -7994,14 +8019,20 @@ The address of the previous objects may have moved and the `MOVE` operator is not used in this case. In case of allocation error, it returns NULL. +`context` parameter is the memory context parameter as provided by the user. +It only exists if the library is built with `M_USE_CONTEXT` + The default used function is the `realloc` function of the LIBC. -##### `void M_MEMORY_FREE (type, ptr, number)` +##### `void M_MEMORY_FREE (context, type, ptr, number)` Delete the cleared array of `number` objects of type `type pointed by the pointer `ptr`. The pointer was previously allocated by the macro `M_MEMORY_REALLOC`. If `ptr` is NULL, the function shall do nothing. +`context` parameter is the memory context parameter as provided by the user. +It only exists if the library is built with `M_USE_CONTEXT` + The default used function is the `free` function of the LIBC. ##### `void M_MEMORY_FULL (type, number)` @@ -9591,8 +9622,8 @@ It is possible to give a custom memory context parameter to the memory methods ` For this, you need to define `M_USE_CONTEXT` to the type of the custom memory context parameter. The definition of this global parameter modifies the generation of **all** functions so that the functions that may allocate or free something will add a new argument, -the memory context parameter `m_context` of type `M_USE_CONTEXT`, -as their first arguement. +the memory context parameter `m_context` of the user provided type `M_USE_CONTEXT`, +as their first argument. It will also modifies the `OPLIST` definition so that they request the parameter named `m_context` for such methods. @@ -9601,29 +9632,26 @@ The memory context can be an [arena allocator](https://www.rfleury.com/p/untangl > [!Note] > The memory context parameter is not stored in the container > because it is not efficient in memory usage and is redundant when you -> create container of other containers (example `array` of `string_t)` -> The user shall ensure of the coherence of the memory context for a given container -> so that it is the same memory context since its construction to its destruction. +> create container of other containers (example `array` of `string_t`). + +The user shall ensure of the coherence of the memory context for a given container +so that the same memory context is used from its construction to its destruction. If you master your own memory, you may want to control when performing the Garbage Collecting. This Garbage Collecting may be quite efficient in reclaiming allocated memory in a single and efficient pass: in such a case, you may not want calling the destructors of the containers as their operation of freeing the memory back to the memory allocator will be redundant and inefficient. As a consequence, the following guarantee is ensured: + The destructors of all containers except the thread safe ones only free back memory. As such, it is safe not to call the destructors of these containers if you have another way to reclaim your memory. -If the destructor of a mutex is also a no-op on your system, this guarantee is extended to the thread safe containers too. +If the destructor of a mutex is also a no-op on your system, this guarantee is extended to all containers +(including the thread safe containers too). In order to use such feature, you need to: * define globally `M_USE_CONTEXT` with your type. -* define globally an override of the memory functions `M_MEMORY_ALLOC`, `M_MEMORY_DEL`, `M_MEMORY_REALLOC` and `M_MEMORY_FREE`. - -You will realize that the expected definitions of these macros don't expect the memory context parameter. -Instead, the macros themselves shall directly use the named argument `m_context` for this purpose. -This argument is ensure to be defined by the function when using this macro. - -FIXME: Seems bad design when written... +* define globally an override of the memory functions `M_MEMORY_ALLOC`, `M_MEMORY_DEL`, `M_MEMORY_REALLOC` and `M_MEMORY_FREE` to use your context. Example @@ -9634,16 +9662,16 @@ typedef int *context_t; // Define the realloc / free that accepts the memory context. static void *my_realloc(void *ptr, size_t old, size_t new, size_t base, context_t context) { - (void) context; // TODO - (void) old; // TODO + (void) context; // TODO what you want with it. + (void) old; // TODO what you want with it. return realloc(ptr, new*base); } static void my_free(void *ptr, size_t old, size_t base, context_t context) { - (void) context; // TODO - (void) old; // TODO - (void) base; // TODO + (void) context; // TODO what you want with it. + (void) old; // TODO what you want with it. + (void) base; // TODO what you want with it. free(ptr); } @@ -9652,13 +9680,14 @@ static void my_free(void *ptr, size_t old, size_t base, context_t context) // Overloaded memory functions to use the custom ones. // m_context is the name of the memory context parameter -# define M_MEMORY_REALLOC(type, ptr, o, n) my_realloc(ptr, o, n, sizeof (type), m_context) -# define M_MEMORY_FREE(type, ptr, o) my_free(ptr, o, sizeof (type), m_context) +# define M_MEMORY_REALLOC(ctx, type, ptr, o, n) my_realloc(ptr, o, n, sizeof (type), ctx) +# define M_MEMORY_FREE(ctx, type, ptr, o) my_free(ptr, o, sizeof (type), ctx) -# define M_MEMORY_ALLOC(type) my_realloc(NULL, 0, 1, sizeof (type), m_context) -# define M_MEMORY_DEL(ptr) my_free(ptr, 1, sizeof (*ptr), m_context) +# define M_MEMORY_ALLOC(ctx, type) my_realloc(NULL, 0, 1, sizeof (type), ctx) +# define M_MEMORY_DEL(ctx, ptr) my_free(ptr, 1, sizeof (*ptr), ctx) // Now you may include M*LIB headers. +#include "m-array.h" ``` This works great for local allocators for a thread. However some containers are designed to handle @@ -9667,9 +9696,9 @@ objects accross several threads: shared pointer, communication queue, ... For such containers, a local allocator has no meaning: it will be very hard to ensure the same allocator is used for both the producor thread and the user threads. -As a concequence, shared pointer, communication queue container and all thread containers +As a consequence, shared pointer, communication queue container and all thread containers will use a global memory context: -M\*LIB won't add a new argument `m_context` to the functions of such containers. Instead it will generate +M\*LIB won't add a context argument to the functions of such containers. Instead it will generate a local parameter initialize to the global memory context. The global memory context is defined by the macro `M_USE_GLOBAL_CONTEXT` which shall define and initialize the named argument to the global memory context. In the absence of definition of such macro, the default is diff --git a/doc/API.txt b/doc/API.txt index 4fe94bd1..0c17fd07 100644 --- a/doc/API.txt +++ b/doc/API.txt @@ -90,6 +90,7 @@ V0.8.0 : * The prototypes of REALLOC / FREE operators are incompatible: Add the old number of elements of the array as argument of REALLOC Add the number of elements of the array and its base type as arguments of FREE. (You can ignore the new arguments which are only hints provided to memory functions). + Add the memory context argument. Also impact M_MEMORY_REALLOC & M_MEMORY_FREE This allows more and efficient integration into custom allocators. * M_MEMORY_FULL: replace the failed size allocation size argument diff --git a/example/ex-alloc1.c b/example/ex-alloc1.c index 49390dae..c20b361f 100644 --- a/example/ex-alloc1.c +++ b/example/ex-alloc1.c @@ -43,12 +43,12 @@ static void my_array_free(void *ptr) //////////////////////////////////////////////////////////// // Allocate for single object -# define M_MEMORY_ALLOC(type) my_alloc (sizeof (type)) -# define M_MEMORY_DEL(ptr) my_free(ptr, sizeof *ptr) +# define M_MEMORY_ALLOC(ctx, type) my_alloc (sizeof (type)) +# define M_MEMORY_DEL(ctx, ptr) my_free(ptr, sizeof *ptr) // Allocate for array of objects -# define M_MEMORY_REALLOC(type, ptr, o, n) my_array_alloc (sizeof (type), ptr, n) -# define M_MEMORY_FREE(type, ptr, n) my_array_free(ptr) +# define M_MEMORY_REALLOC(ctx, type, ptr, o, n) my_array_alloc (sizeof (type), ptr, n) +# define M_MEMORY_FREE(ctx, type, ptr, n) my_array_free(ptr) #include "m-list.h" #include "m-array.h" diff --git a/example/ex-alloc2.c b/example/ex-alloc2.c index 48a441dd..996362ef 100644 --- a/example/ex-alloc2.c +++ b/example/ex-alloc2.c @@ -23,8 +23,8 @@ static void my_free(void *ptr, size_t s) } // Allocate for single object -# define MY_MEMORY_ALLOC(type) my_alloc (sizeof (type)) -# define MY_MEMORY_DEL(ptr) my_free(ptr, sizeof *ptr) +# define MY_MEMORY_ALLOC(ctx, type) my_alloc (sizeof (type)) +# define MY_MEMORY_DEL(ctx, ptr) my_free(ptr, sizeof *ptr) //////////////////////////////////////////////////////////// @@ -76,8 +76,8 @@ static void my_array_free(void *ptr) } // Allocate for array of objects -# define MY_MEMORY_REALLOC(type, ptr, o, n) my_array_alloc (sizeof (type), ptr, n) -# define MY_MEMORY_FREE(type, ptr, n) my_array_free(ptr) +# define MY_MEMORY_REALLOC(ctx, type, ptr, o, n) my_array_alloc (sizeof (type), ptr, n) +# define MY_MEMORY_FREE(ctx, type, ptr, n) my_array_free(ptr) //////////////////////////////////////////////////////////// diff --git a/example/ex11-use-pool.c b/example/ex11-use-pool.c index 00c76544..3e11f85e 100644 --- a/example/ex11-use-pool.c +++ b/example/ex11-use-pool.c @@ -23,12 +23,11 @@ static void my_free(void *ptr, size_t old, size_t base, context_t context) # define M_USE_CONTEXT context_t // Overloaded memory functions to use the custom ones. -// m_context is the name of the memory context parameter -# define M_MEMORY_REALLOC(type, ptr, o, n) my_realloc(ptr, o, n, sizeof (type), m_context) -# define M_MEMORY_FREE(type, ptr, o) my_free(ptr, o, sizeof (*ptr), m_context) +# define M_MEMORY_REALLOC(ctx, type, ptr, o, n) my_realloc(ptr, o, n, sizeof (type), ctx) +# define M_MEMORY_FREE(ctx, type, ptr, o) my_free(ptr, o, sizeof (*ptr), ctx) -# define M_MEMORY_ALLOC(type) my_realloc(NULL, 0, 1, sizeof (type), m_context) -# define M_MEMORY_DEL(ptr) my_free(ptr, 1, sizeof (*ptr), m_context) +# define M_MEMORY_ALLOC(ctx, type) my_realloc(NULL, 0, 1, sizeof (type), ctx) +# define M_MEMORY_DEL(ctx, ptr) my_free(ptr, 1, sizeof (*ptr), ctx) // Now we can include M*LIB functions. #include "m-string.h" diff --git a/m-bitset.h b/m-bitset.h index fdeb0ace..4c346054 100644 --- a/m-bitset.h +++ b/m-bitset.h @@ -104,7 +104,7 @@ m_bitset_reset(m_bitset_t t) M_P(void, m_bitset, _clear, m_bitset_t t) { m_bitset_reset(t); - M_MEMORY_FREE(m_b1tset_limb_ct, t->ptr, t->alloc); + M_MEMORY_FREE(m_context, m_b1tset_limb_ct, t->ptr, t->alloc); // This is not really needed, but is safer // This representation is invalid and will be detected by the contract. // A C compiler should be able to optimize out theses initializations. @@ -122,7 +122,7 @@ M_P(void, m_bitset, _set, m_bitset_t d, const m_bitset_t s) if (M_LIKELY (s->size > 0)) { // Test if enough space in target if (s->size > M_B1TSET_FROM_ALLOC (d->alloc)) { - m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_b1tset_limb_ct, d->ptr, d->alloc, needAlloc); + m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_context, m_b1tset_limb_ct, d->ptr, d->alloc, needAlloc); if (M_UNLIKELY_NOMEM (ptr == NULL)) { M_MEMORY_FULL(m_b1tset_limb_ct, needAlloc); } @@ -209,7 +209,7 @@ M_P(void, m_bitset, _push_back, m_bitset_t v, bool x) M_MEMORY_FULL(m_b1tset_limb_ct, needAlloc); } // Alloc memory - m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_b1tset_limb_ct, v->ptr, v->alloc, needAlloc); + m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_context, m_b1tset_limb_ct, v->ptr, v->alloc, needAlloc); // Check if success if (M_UNLIKELY_NOMEM (ptr == NULL) ) { M_MEMORY_FULL(m_b1tset_limb_ct, needAlloc); @@ -245,7 +245,7 @@ M_P(void, m_bitset, _resize, m_bitset_t v, size_t size) size_t newAlloc = M_B1TSET_TO_ALLOC (size); if (newAlloc > v->alloc) { // Allocate more limbs to store the bitset. - m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_b1tset_limb_ct, v->ptr, v->alloc, newAlloc); + m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_context, m_b1tset_limb_ct, v->ptr, v->alloc, newAlloc); if (M_UNLIKELY_NOMEM (ptr == NULL) ) { M_MEMORY_FULL(m_b1tset_limb_ct, newAlloc); } @@ -290,12 +290,12 @@ M_P(void, m_bitset, _reserve, m_bitset_t v, size_t alloc) } if (M_UNLIKELY (newAlloc == 0)) { // Free all memory used by the bitsets - M_MEMORY_FREE (m_b1tset_limb_ct, v->ptr, v->alloc); + M_MEMORY_FREE (m_context, m_b1tset_limb_ct, v->ptr, v->alloc); v->size = v->alloc = 0; v->ptr = NULL; } else { // Allocate more memory or reduce memory usage - m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_b1tset_limb_ct, v->ptr, v->alloc, newAlloc); + m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_context, m_b1tset_limb_ct, v->ptr, v->alloc, newAlloc); if (M_UNLIKELY_NOMEM (ptr == NULL) ) { M_MEMORY_FULL(m_b1tset_limb_ct, newAlloc); } diff --git a/m-bstring.h b/m-bstring.h index 60f30059..4d956f46 100644 --- a/m-bstring.h +++ b/m-bstring.h @@ -77,7 +77,7 @@ m_bstring_init(m_bstring_t s) M_P(void, m_bstring, _clear, m_bstring_t s) { M_BSTRING_CONTRACT(s); - M_MEMORY_FREE(uint8_t, s->ptr, s->alloc); + M_MEMORY_FREE(m_context, uint8_t, s->ptr, s->alloc); s->offset = (size_t) -1; } @@ -164,7 +164,7 @@ M_P(uint8_t *, m_bstr1ng, _fit2size, m_bstring_t v, size_t size_alloc, bool exac if (M_UNLIKELY_NOMEM (alloc < size_alloc)) { goto allocation_error; } - uint8_t *ptr = M_MEMORY_REALLOC (uint8_t, v->ptr, v->alloc, alloc); + uint8_t *ptr = M_MEMORY_REALLOC (m_context, uint8_t, v->ptr, v->alloc, alloc); if (M_UNLIKELY_NOMEM (ptr == NULL)) { goto allocation_error; } @@ -396,13 +396,13 @@ M_P(void, m_bstring, _reserve, m_bstring_t v, size_t n) n = v->offset + size; } if (n == 0) { - M_MEMORY_FREE(uint8_t, v->ptr, v->alloc); + M_MEMORY_FREE(m_context, uint8_t, v->ptr, v->alloc); v->offset = 0; v->size = 0; v->alloc = 0; v->ptr = NULL; } else { - uint8_t *ptr = M_MEMORY_REALLOC (uint8_t, v->ptr, v->alloc, n); + uint8_t *ptr = M_MEMORY_REALLOC (m_context, uint8_t, v->ptr, v->alloc, n); if (M_UNLIKELY_NOMEM (ptr == NULL)) { M_MEMORY_FULL(uint8_t, n); } diff --git a/m-c-mempool.h b/m-c-mempool.h index d2059016..e144e496 100644 --- a/m-c-mempool.h +++ b/m-c-mempool.h @@ -97,7 +97,7 @@ M_BEGIN_PROTECTED_CODE M_F(name, _slist_node_ct) *it = *list, *next; \ while (it) { \ next = it->next; \ - M_MEMORY_DEL(it); \ + M_MEMORY_DEL(m_context, it); \ it = next; \ } \ *list = NULL; \ @@ -292,14 +292,14 @@ M_BEGIN_PROTECTED_CODE M_F(name, _lf_node_t) *next = atomic_load_explicit(&node->next, \ memory_order_relaxed); \ M_F(name, _slist_clear)(node->list); \ - M_MEMORY_DEL(node); \ + M_MEMORY_DEL(m_context, node); \ node = next; \ } \ /* Dummy node to free too */ \ M_F(name, _lf_node_t) *dummy; \ dummy = atomic_load_explicit(&list->head, memory_order_relaxed); \ M_F(name, _slist_clear)(dummy->list); \ - M_MEMORY_DEL(dummy); \ + M_MEMORY_DEL(m_context, dummy); \ } \ /* System node allocator: request memory to the system. @@ -310,7 +310,7 @@ M_BEGIN_PROTECTED_CODE M_F(name, _alloc_node)(unsigned int initial) \ { \ M_F(name, _lf_node_t) * node; \ - node = M_MEMORY_ALLOC(M_F(name, _lf_node_t)); \ + node = M_MEMORY_ALLOC(m_context, M_F(name, _lf_node_t)); \ if (M_UNLIKELY_NOMEM (node == NULL)) { \ M_MEMORY_FULL(M_F(name, _lf_node_t), 1); \ } \ @@ -319,7 +319,7 @@ M_BEGIN_PROTECTED_CODE M_F(name, _slist_init)(node->list); \ for(unsigned i = 0; i < initial; i++) { \ M_F(name, _slist_node_ct) *n; \ - n = M_MEMORY_ALLOC(M_F(name, _slist_node_ct)); \ + n = M_MEMORY_ALLOC(m_context, M_F(name, _slist_node_ct)); \ if (M_UNLIKELY_NOMEM (n == NULL)) { \ M_MEMORY_FULL(M_F(name, _lf_node_t), 1); \ } \ @@ -462,7 +462,7 @@ M_BEGIN_PROTECTED_CODE { \ const size_t max_thread = gc_mem->max_thread; \ /* Initialize the thread data of the mempool */ \ - mem->thread_data = M_MEMORY_REALLOC(M_F(name, _lfmp_thread_ct), NULL, 0, max_thread); \ + mem->thread_data = M_MEMORY_REALLOC(m_context, M_F(name, _lfmp_thread_ct), NULL, 0, max_thread); \ if (M_UNLIKELY_NOMEM (mem->thread_data == NULL)) { \ M_MEMORY_FULL(M_F(name, _lfmp_thread_ct), max_thread); \ } \ @@ -494,7 +494,7 @@ M_BEGIN_PROTECTED_CODE for(unsigned i = 0; i < max_thread;i++) { \ M_F(name, _lfmp_thread_clear)(&mem->thread_data[i]); \ } \ - M_MEMORY_FREE(M_F(name, _lfmp_thread_ct), mem->thread_data, max_thread); \ + M_MEMORY_FREE(m_context, M_F(name, _lfmp_thread_ct), mem->thread_data, max_thread); \ mem->thread_data = NULL; \ M_F(name, _lflist_clear)(mem->empty); \ M_F(name, _lflist_clear)(mem->free); \ @@ -584,7 +584,7 @@ m_gc_init(m_gc_t gc_mem, size_t max_thread) atomic_init(&gc_mem->ticket, 0UL); m_genint_init(gc_mem->thread_alloc, (unsigned int) max_thread); - gc_mem->thread_data = M_MEMORY_REALLOC(m_gc_lfmp_thread_ct, NULL, 0, max_thread); + gc_mem->thread_data = M_MEMORY_REALLOC(m_context, m_gc_lfmp_thread_ct, NULL, 0, max_thread); if (M_UNLIKELY_NOMEM (gc_mem->thread_data == NULL)) { M_MEMORY_FULL(m_gc_lfmp_thread_ct, max_thread); } @@ -604,7 +604,7 @@ m_gc_clear(m_gc_t gc_mem) for(m_gc_tid_t i = 0; i < gc_mem->max_thread;i++) { m_core_backoff_clear(gc_mem->thread_data[i].bkoff); } - M_MEMORY_FREE(m_gc_lfmp_thread_ct, gc_mem->thread_data, gc_mem->max_thread); + M_MEMORY_FREE(m_context, m_gc_lfmp_thread_ct, gc_mem->thread_data, gc_mem->max_thread); gc_mem->thread_data = NULL; m_genint_clear(gc_mem->thread_alloc); } @@ -753,7 +753,7 @@ m_vlapool_init(m_vlapool_t mem, m_gc_t gc_mem) const size_t max_thread = gc_mem->max_thread; /* Initialize the thread data of the vlapool */ - mem->thread_data = M_MEMORY_REALLOC(m_vlapool_lfmp_thread_ct, NULL, 0, max_thread); + mem->thread_data = M_MEMORY_REALLOC(m_context, m_vlapool_lfmp_thread_ct, NULL, 0, max_thread); if (M_UNLIKELY_NOMEM (mem->thread_data == NULL)) { M_MEMORY_FULL(m_vlapool_lfmp_thread_ct, max_thread); } @@ -779,7 +779,7 @@ m_vlapool_clear(m_vlapool_t mem) for(unsigned i = 0; i < max_thread;i++) { m_vlapool_lfmp_thread_clear(&mem->thread_data[i]); } - M_MEMORY_FREE(m_vlapool_lfmp_thread_ct, mem->thread_data, max_thread); + M_MEMORY_FREE(m_context, m_vlapool_lfmp_thread_ct, mem->thread_data, max_thread); mem->thread_data = NULL; m_vlapool_lflist_clear(mem->empty); M_ASSERT(m_vlapool_lflist_empty_p(mem->to_be_reclaimed)); @@ -802,7 +802,7 @@ m_vlapool_new(m_vlapool_t mem, m_gc_tid_t id, size_t size) size += offsetof(struct m_vlapool_slist_node_s, data); // Simply wrap around a system call to get the memory - char *ptr = M_MEMORY_REALLOC(char, NULL, 0, size); + char *ptr = M_MEMORY_REALLOC(m_context, char, NULL, 0, size); // TODO: save the allocated size somewhere (create a new field in vla) // FIXME: Bug, we call DEL operator to free the node, but we have allocated it with the REALLOC one! // And they are not necessarily compatible as per the spec (even if they are if they both use 'free') diff --git a/m-core.h b/m-core.h index 8228bace..3a8eccb5 100644 --- a/m-core.h +++ b/m-core.h @@ -363,39 +363,47 @@ M_BEGIN_PROTECTED_CODE explicit cast) */ /* Define allocators for object: - * void *M_MEMORY_ALLOC(type): Return a pointer to a new object of type 'type' + * void *M_MEMORY_ALLOC(ctx, type): + * Return a pointer to a new object of type 'type' * It returns NULL in case of memory allocation failure. - * void M_MEMORY_DEL(ptr): Free the object associated to the pointer. + * void M_MEMORY_DEL(ctx, ptr): + * Free the object associated to the pointer. + * By default, 'context' does nothing. + * 'context' is the context parameter given to the memory functions to identify where to create the object. + * It exists only if M_USE_CONTEXT has been globally defined. */ #ifndef M_MEMORY_ALLOC #ifdef __cplusplus # include -# define M_MEMORY_ALLOC(type) ((type*)std::malloc (sizeof (type))) -# define M_MEMORY_DEL(ptr) std::free(ptr) +# define M_MEMORY_ALLOC(ctx, type) ((type*)std::malloc (sizeof (type))) +# define M_MEMORY_DEL(ctx, ptr) std::free(ptr) #else -# define M_MEMORY_ALLOC(type) malloc (sizeof (type)) -# define M_MEMORY_DEL(ptr) free(ptr) +# define M_MEMORY_ALLOC(ctx, type) malloc (sizeof (type)) +# define M_MEMORY_DEL(ctx, ptr) free(ptr) #endif #endif /* Define allocators for array - * void *M_MEMORY_REALLOC(type, ptr, o, n, perm): Return a pointer to a new array of 'n' object of type 'type'. + * void *M_MEMORY_REALLOC(context, type, ptr, o, n, perm): + * Return a pointer to a new array of 'n' object of type 'type'. * If ptr is NULL, it creates a new array of type 'type' and size 'n' * If ptr is not null, it reallocates the given array of type 'type' and size 'o' to the new size. * It returns NULL in case of memory allocation failure. - * void M_MEMORY_FREE(type, ptr, o): Free the object associated to the array of type 'type' at base 'ptr' of size 'o'. - * By default, old size do nothing. - * Can use local optional named parameter "m_context" which is defined by the called of the macro if M_USE_CONTEXT is defined. + * void M_MEMORY_FREE(type, ptr, o): + * Free the object associated to the array of type 'type' at base 'ptr' of size 'o'. + * By default, 'o' size and 'context' do nothing. + * 'context' is the context parameter given to the memory functions to identify where to create the object. + * It exists only if M_USE_CONTEXT has been globally defined. */ #ifndef M_MEMORY_REALLOC #ifdef __cplusplus # include -# define M_MEMORY_REALLOC(type, ptr, o, n) \ +# define M_MEMORY_REALLOC(ctx, type, ptr, o, n) \ ((type*) (M_UNLIKELY ((n) > SIZE_MAX / sizeof(type)) ? NULL : std::realloc ((ptr), (n)*sizeof (type)))) -# define M_MEMORY_FREE(type, ptr, o) std::free(ptr) +# define M_MEMORY_FREE(ctx, type, ptr, o) std::free(ptr) #else -# define M_MEMORY_REALLOC(type, ptr, o, n) (M_UNLIKELY ((n) > SIZE_MAX / sizeof(type)) ? NULL : realloc ((ptr), (n)*sizeof (type))) -# define M_MEMORY_FREE(type, ptr, o) free(ptr) +# define M_MEMORY_REALLOC(ctx, type, ptr, o, n) (M_UNLIKELY ((n) > SIZE_MAX / sizeof(type)) ? NULL : realloc ((ptr), (n)*sizeof (type))) +# define M_MEMORY_FREE(ctx, type, ptr, o) free(ptr) #endif #endif @@ -3632,7 +3640,6 @@ M_INLINE size_t m_core_cstr_hash(const char str[]) #define M_CALL_MOVE(oplist, ...) M_APPLY_API(M_GET_MOVE oplist, oplist, __VA_ARGS__) #define M_CALL_SWAP(oplist, ...) M_APPLY_API(M_GET_SWAP oplist, oplist, __VA_ARGS__) #define M_CALL_CLEAR(oplist, ...) M_APPLY_API(M_GET_CLEAR oplist, oplist, __VA_ARGS__) -#define M_CALL_NEW(oplist, ...) M_APPLY_API(M_GET_NEW oplist, oplist, __VA_ARGS__) #define M_CALL_HASH(oplist, ...) M_APPLY_API(M_GET_HASH oplist, oplist, __VA_ARGS__) #define M_CALL_EQUAL(oplist, ...) M_APPLY_API(M_GET_EQUAL oplist, oplist, __VA_ARGS__) #define M_CALL_CMP(oplist, ...) M_APPLY_API(M_GET_CMP oplist, oplist, __VA_ARGS__) @@ -3691,9 +3698,10 @@ M_INLINE size_t m_core_cstr_hash(const char str[]) //#define M_CALL_PROPERTIES(oplist, ...) M_APPLY_API(M_GET_PROPERTIES oplist, oplist, __VA_ARGS__) //#define M_CALL_EMPLACE_TYPE(oplist, ...) M_APPLY_API(M_GET_EMPLACE_TYPE oplist, oplist, __VA_ARGS__) // As attribute customization -#define M_CALL_DEL(oplist, ...) M_APPLY_API(M_GET_DEL oplist, oplist, __VA_ARGS__) -#define M_CALL_REALLOC(oplist, ...) M_APPLY_API(M_GET_REALLOC oplist, oplist, __VA_ARGS__) -#define M_CALL_FREE(oplist, ...) M_APPLY_API(M_GET_FREE oplist, oplist, __VA_ARGS__) +#define M_CALL_NEW(oplist, ...) M_APPLY_API(M_GET_NEW oplist, oplist, m_context, __VA_ARGS__) +#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__) diff --git a/m-deque.h b/m-deque.h index 01c9b119..42cfec4c 100644 --- a/m-deque.h +++ b/m-deque.h @@ -104,9 +104,9 @@ // OPLIST for the node deletion #ifdef M_USE_CONTEXT -#define M_D3QU3_NODE_DEL_OPLIST(name) (DEL(API_0P(M_F(name, _node_list_i_del)))) -#else #define M_D3QU3_NODE_DEL_OPLIST(name) (DEL(M_F(name, _node_list_i_del))) +#else +#define M_D3QU3_NODE_DEL_OPLIST(name) (DEL(API(M_F(name, _node_list_i_del), NONE, ARG2))) #endif /* Define the types. diff --git a/m-genint.h b/m-genint.h index 49b6a8ae..d65eebb2 100644 --- a/m-genint.h +++ b/m-genint.h @@ -108,7 +108,7 @@ M_P(void, m_genint, _init, m_genint_t s, unsigned int n) M_ASSERT (s != NULL && n > 0 && n <= M_GENINT_MAX_ALLOC); const size_t alloc = (n + M_GEN1NT_LIMBSIZE - 1) / M_GEN1NT_LIMBSIZE; const unsigned int index = n % M_GEN1NT_LIMBSIZE; - atomic_ullong *ptr = M_MEMORY_REALLOC (atomic_ullong, NULL, 0, alloc); + atomic_ullong *ptr = M_MEMORY_REALLOC (m_context, atomic_ullong, NULL, 0, alloc); if (M_UNLIKELY_NOMEM (ptr == NULL)) { M_MEMORY_FULL(atomic_ullong, alloc); } @@ -127,7 +127,7 @@ M_P(void, m_genint, _init, m_genint_t s, unsigned int n) M_P(void, m_genint, _clear, m_genint_t s) { M_GEN1NT_CONTRACT(s); - M_MEMORY_FREE(atomic_ullong, s->data, s->max+1); + M_MEMORY_FREE(m_context, atomic_ullong, s->data, s->max+1); s->data = NULL; } diff --git a/m-mempool.h b/m-mempool.h index c8d9b301..b7017a73 100644 --- a/m-mempool.h +++ b/m-mempool.h @@ -116,7 +116,7 @@ M_F(name,_init)(name_t mem) \ { \ mem->free_list = NULL; \ - mem->current_segment = M_MEMORY_ALLOC(M_F(name,_segment_ct)); \ + 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); \ } \ @@ -132,7 +132,7 @@ M_F(name,_segment_ct) *segment = mem->current_segment; \ while (segment != NULL) { \ M_F(name,_segment_ct) *next = segment->next; \ - M_MEMORY_DEL (segment); \ + M_MEMORY_DEL (m_context, segment); \ segment = next; \ } \ /* Clean pointers to be safer */ \ @@ -157,7 +157,7 @@ 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_F(name,_segment_ct)); \ + 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); \ } \ diff --git a/m-snapshot.h b/m-snapshot.h index 7e307811..72e595f5 100644 --- a/m-snapshot.h +++ b/m-snapshot.h @@ -390,7 +390,7 @@ M_P(void, m_snapsh0t, _mrsw_init, m_snapsh0t_mrsw_ct s, size_t n) n += M_SNAPSH0T_SPMC_EXTRA_BUFFER; // Initialize the counters to zero (no reader use it) - atomic_uint *ptr = M_MEMORY_REALLOC (atomic_uint, NULL, 0, n); + atomic_uint *ptr = M_MEMORY_REALLOC (m_context, atomic_uint, NULL, 0, n); if (M_UNLIKELY_NOMEM (ptr == NULL)) { M_MEMORY_FULL(atomic_uint, n); } @@ -416,7 +416,7 @@ M_P(void, m_snapsh0t, _mrsw_init, m_snapsh0t_mrsw_ct s, size_t n) M_P(void, m_snapsh0t, _mrsw_clear, m_snapsh0t_mrsw_ct s) { M_SNAPSH0T_SPMC_INT_CONTRACT(s); - M_MEMORY_FREE (atomic_uint, s->cptTab, s->n_reader+M_SNAPSH0T_SPMC_EXTRA_BUFFER); + M_MEMORY_FREE (m_context, atomic_uint, s->cptTab, s->n_reader+M_SNAPSH0T_SPMC_EXTRA_BUFFER); m_genint_clear M_R(s->freeList); s->cptTab = NULL; s->n_reader = 0; diff --git a/m-string.h b/m-string.h index 8f5259bf..03e7efe0 100644 --- a/m-string.h +++ b/m-string.h @@ -285,7 +285,7 @@ M_P(void, m_string, _clear, m_string_t v) { M_STR1NG_CONTRACT(v); if (!m_str1ng_embedded_p(v)) { - M_MEMORY_FREE(char, v->u.heap.ptr, m_string_capacity(v)); + M_MEMORY_FREE(m_context, char, v->u.heap.ptr, m_string_capacity(v)); v->u.heap.ptr = NULL; } /* This is not needed but is safer to make @@ -309,7 +309,7 @@ M_P(char *, m_string, _clear_get_cstr, m_string_t v) p = &v->u.buffer[0]; // Need to allocate a heap string to return the copy. size_t alloc = m_string_size(v)+1; - char *ptr = M_MEMORY_REALLOC (char, NULL, 0, alloc); + char *ptr = M_MEMORY_REALLOC (m_context, char, NULL, 0, alloc); if (M_UNLIKELY_NOMEM (ptr == NULL)) { M_MEMORY_FULL(char, alloc); } @@ -386,7 +386,7 @@ M_P(char *, m_str1ng, _fit2size, m_string_t v, size_t size_alloc) } char *ptr = m_str1ng_embedded_p(v) ? NULL : v->u.heap.ptr; //FIXME: old_alloc may not be NULL if ptr is NULL. - ptr = M_MEMORY_REALLOC (char, ptr, old_alloc, alloc); + ptr = M_MEMORY_REALLOC (m_context, char, ptr, old_alloc, alloc); if (M_UNLIKELY_NOMEM (ptr == NULL)) { M_MEMORY_FULL(char, alloc); } @@ -429,7 +429,7 @@ M_P(void, m_string, _reserve, m_string_t v, size_t alloc) char *oldptr = v->u.heap.ptr; memcpy(ptr, oldptr, size+1); // Free the allocated memory on the heap - M_MEMORY_FREE(char, oldptr, m_string_capacity(v)); + M_MEMORY_FREE(m_context, char, oldptr, m_string_capacity(v)); v->u.buffer[sizeof(m_string_t)-2] = 0; v->u.buffer[sizeof(m_string_t)-1] = (char) size; } else { @@ -444,7 +444,7 @@ M_P(void, m_string, _reserve, m_string_t v, size_t alloc) M_MEMORY_FULL(char, alloc); } char *ptr = m_str1ng_embedded_p(v) ? NULL : v->u.heap.ptr; - ptr = M_MEMORY_REALLOC (char, ptr, m_string_capacity(v), r_alloc); + ptr = M_MEMORY_REALLOC (m_context, char, ptr, m_string_capacity(v), r_alloc); if (M_UNLIKELY_NOMEM (ptr == NULL) ) { M_MEMORY_FULL(char, alloc); } diff --git a/m-worker.h b/m-worker.h index 2bf0244f..4e533f92 100644 --- a/m-worker.h +++ b/m-worker.h @@ -237,7 +237,7 @@ typedef struct m_worker_sync_s { M_GLOBAL_CONTEXT(); \ M_MAP3(M_WORK3R_SPAWN_EXTEND_DEF_CALLBACK_CLEAR, data, __VA_ARGS__) \ /* TODO: Overload */ \ - M_MEMORY_DEL(p); \ + M_MEMORY_DEL(m_context, p); \ } \ \ M_INLINE void \ @@ -265,7 +265,7 @@ typedef struct m_worker_sync_s { { \ M_GLOBAL_CONTEXT(); \ if (!m_work3r_queue_full_p(block->worker->queue_g)) { \ - struct M_C3(m_worker_, name, _s) *p = M_MEMORY_ALLOC ( struct M_C3(m_worker_, name, _s)); \ + struct M_C3(m_worker_, name, _s) *p = M_MEMORY_ALLOC (m_context, struct M_C3(m_worker_, name, _s)); \ if (M_UNLIKELY_NOMEM(p == NULL)) { \ M_MEMORY_FULL(struct M_C3(m_worker_, name, _s), 1); \ } \ @@ -412,7 +412,7 @@ m_worker_init(m_worker_t g, int numWorker, unsigned int extraQueue, void (*reset M_ASSERT(numWorker >= 0); size_t numWorker_st = (size_t) numWorker; M_GLOBAL_CONTEXT(); - g->worker = M_MEMORY_REALLOC(m_work3r_thread_ct, NULL, 0, numWorker_st); + g->worker = M_MEMORY_REALLOC(m_context, m_work3r_thread_ct, NULL, 0, numWorker_st); if (M_UNLIKELY_NOMEM (g->worker == NULL)) { M_MEMORY_FULL(m_work3r_thread_ct, numWorker_st); } @@ -457,7 +457,7 @@ m_worker_clear(m_worker_t g) m_thread_join(g->worker[i].id); } // Clear memory - M_MEMORY_FREE(m_work3r_thread_ct, g->worker, g->numWorker_g); + M_MEMORY_FREE(m_context, m_work3r_thread_ct, g->worker, g->numWorker_g); m_mutex_clear(g->lock); m_cond_clear(g->a_thread_ends); m_work3r_queue_clear (g->queue_g); diff --git a/tests/test-mbuffer.c b/tests/test-mbuffer.c index 80b14a6f..a9077c06 100644 --- a/tests/test-mbuffer.c +++ b/tests/test-mbuffer.c @@ -271,7 +271,7 @@ typedef struct test_s { static void test_init(test_t *p) { memset(p->buffer, 0x00, 52); } static void test_clear(test_t *p) { memset(p->buffer, 0xFF, 52); } ISHARED_PTR_DEF(ishared_itest, test_t, - (INIT(test_init M_IPTR), CLEAR(test_clear M_IPTR), DEL(free))) + (INIT(test_init M_IPTR), CLEAR(test_clear M_IPTR), DEL(API(free, NONE, ARG2)))) typedef struct test2_s { ISHARED_PTR_INTERFACE(ishared_itest2, struct test_s); diff --git a/tests/test-milist.c b/tests/test-milist.c index df3a3849..939ef36f 100644 --- a/tests/test-milist.c +++ b/tests/test-milist.c @@ -41,7 +41,7 @@ START_COVERAGE ILIST_DEF(ilist_tname, test_t, M_OPEXTEND(M_POD_OPLIST, EQUAL(API_6(test_equal_p)))) END_COVERAGE -ILIST_DEF(ilist_free, test_t, (DEL(free))) +ILIST_DEF(ilist_free, test_t, (DEL(API(free, NONE, ARG2)))) typedef struct testd_s { double n; diff --git a/tests/test-obj-except.h b/tests/test-obj-except.h index e4aaa0db..e3086607 100644 --- a/tests/test-obj-except.h +++ b/tests/test-obj-except.h @@ -37,16 +37,16 @@ static inline void test_obj_except_trigger(void); extern int test_obj_except__malloc_counter; #ifdef __cplusplus # include -# define M_MEMORY_ALLOC(type) (test_obj_except_trigger(), test_obj_except__malloc_counter++, (type*)std::malloc (sizeof (type))) -# define M_MEMORY_DEL(ptr) (test_obj_except__malloc_counter-=(ptr != NULL), std::free(ptr)) -# define M_MEMORY_REALLOC(type, ptr, o, n) \ +# define M_MEMORY_ALLOC(ctx, type) (test_obj_except_trigger(), test_obj_except__malloc_counter++, (type*)std::malloc (sizeof (type))) +# define M_MEMORY_DEL(ctx, ptr) (test_obj_except__malloc_counter-=(ptr != NULL), std::free(ptr)) +# define M_MEMORY_REALLOC(ctx, type, ptr, o, n) \ ((type*) (test_obj_except_trigger(), test_obj_except__malloc_counter+= (ptr == NULL), std::realloc ((ptr), (n)*sizeof (type)))) -# define M_MEMORY_FREE(type, ptr, o) (test_obj_except__malloc_counter-=(ptr != NULL), std::free(ptr)) +# define M_MEMORY_FREE(ctx, type, ptr, o) (test_obj_except__malloc_counter-=(ptr != NULL), std::free(ptr)) #else -# define M_MEMORY_ALLOC(type) (test_obj_except_trigger(), test_obj_except__malloc_counter++, malloc (sizeof (type))) -# define M_MEMORY_DEL(ptr) (test_obj_except__malloc_counter-=(ptr != NULL), free(ptr)) -# define M_MEMORY_REALLOC(type, ptr, o, n) (test_obj_except_trigger(), test_obj_except__malloc_counter+=(ptr == NULL), realloc ((ptr), (n)*sizeof (type))) -# define M_MEMORY_FREE(type, ptr, o) (test_obj_except__malloc_counter-=(ptr != NULL), free(ptr)) +# define M_MEMORY_ALLOC(ctx, type) (test_obj_except_trigger(), test_obj_except__malloc_counter++, malloc (sizeof (type))) +# define M_MEMORY_DEL(ctx, ptr) (test_obj_except__malloc_counter-=(ptr != NULL), free(ptr)) +# define M_MEMORY_REALLOC(ctx, type, ptr, o, n) (test_obj_except_trigger(), test_obj_except__malloc_counter+=(ptr == NULL), realloc ((ptr), (n)*sizeof (type))) +# define M_MEMORY_FREE(ctx, type, ptr, o) (test_obj_except__malloc_counter-=(ptr != NULL), free(ptr)) #endif // Include M*LIB headers diff --git a/tests/test-obj.h b/tests/test-obj.h index 42f73df1..fc6a5944 100644 --- a/tests/test-obj.h +++ b/tests/test-obj.h @@ -68,8 +68,8 @@ static inline void overloaded_free(size_t st, void *ptr, size_t n) free(p); } -#define M_MEMORY_REALLOC(type, ptr, o, n) (type *) overloaded_realloc(sizeof(type), ptr, o, n) -#define M_MEMORY_FREE(type, ptr, o) overloaded_free(sizeof(type), ptr, o) +#define M_MEMORY_REALLOC(ctx, type, ptr, o, n) (type *) overloaded_realloc(sizeof(type), ptr, o, n) +#define M_MEMORY_FREE(ctx, type, ptr, o) overloaded_free(sizeof(type), ptr, o) //////////////////////////////////////////////////////////////////////////////////////////////////////////////////