Fri, 11 Apr 2025 13:20:07 +0200
add cxMempoolTransfer() - partially resolves #640
# Memory Pool A memory pool is providing an allocator implementation that automatically deallocates the memory upon its destruction. It also allows you to register destructor functions for the allocated memory, which are automatically called before the memory is deallocated. Additionally, you may also register _independent_ destructor functions. This can be useful, for example, when some library allocates memory that you wish to destroy when the memory pool gets destroyed. A memory pool can be used with all UCX features that support the use of an [allocator](allocator.h.md). For example, the UCX [string](string.h.md) functions provide several variants suffixed with `_a` for that purpose. ## Basic Memory Management ```C #include <cx/mempool.h> CxMempool *cxMempoolCreate(size_t capacity, cx_destructor_func fnc); CxMempool *cxMempoolCreateSimple(size_t capacity); void cxMempoolFree(CxMempool *pool); void cxMempoolSetDestructor(void *memory, cx_destructor_func fnc); void cxMempoolRemoveDestructor(void *memory); int cxMempoolRegister(CxMempool *pool, void *memory, cx_destructor_func fnc); ``` A memory pool is created with the `cxMempoolCreate()` function with a default `capacity` and an optional default destructor function `fnc`. If specified, the default destructor function is registered for all freshly allocated memory within the pool, as if `cxMempoolSetDestructor()` was called immediately after allocation. When you set `fnc` is to `NULL` during pool creation, or use `cxMempoolCreateSimple`, no default destructor is registered. After creating a memory pool `CxMempool *mpool`, you can access the provided allocator via `mpool->allocator`. The functions `cxMempoolSetDestructor()` and `cxMempoolRemoveDestructor()` can be used to assign a specific destructor function to an allocated object or remove any assigned destructor function, respectively. The `memory` pointer points to the allocated object, which must have been allocated by any `CxMempool`'s provided allocator. The `cxMempoolRegister()` function allocates a new wrapper object for `memory` with `pool`'s allocator that will call the specified destructor function when destroyed. Usually this function returns zero except for platforms where memory allocations are likely to fail, in which case a non-zero value is returned. ### Order of Destruction When you call `cxMempoolFree()` the following actions are performed: 1. In any order, for each object in the pool 1. the destructor function assigned to that object is called 2. the object's memory is deallocated 2. The pool memory is deallocated 3. The pool structure is deallocated ## Transfer Memory ```C #include <cx/mempool.h> int cxMempoolTransfer(CxMempool *source, CxMempool *dest); ``` Memory managed by a pool can be transferred to another pool. The function `cxMempoolTransfer()` transfers all memory managed and/or registered with the `source` pool to the `dest` pool. It also registers its allocator with the `dest` pool and creates a new allocator for the `source` pool. That means, that all references to the allocator of the `source` pool remain valid and continue to work with the `dest` pool. The transferred allocator will be destroyed when the `dest` pool gets destroyed. The function returns zero when the transfer was successful and non-zero if a necessary memory allocation was not possible, or the `source` and `dest` pointers point to the same pool. In case of an error, no memory is transferred and both pools are in a valid state. > Although the allocator from the `source` pool remains valid for the already allocated objects, > it is **not** valid to use that allocator to allocate new objects in the `dest` pool. > It may only be used to free or reallocate the memory of the existing objects. > > The reason is, that the allocator will be destroyed after destroying all objects from the `source` pool and > _before_ destroying objects in the `dest` pool which were allocated after the transfer. >{style="warning"} ## Example The following code illustrates how the contents of a CSV file are read into pooled memory. ```C #include <stdio.h> #include <cx/mempool.h> #include <cx/linked_list.h> #include <cx/string.h> #include <cx/buffer.h> #include <cx/utils.h> typedef struct { cxstring column_a; cxstring column_b; cxstring column_c; } CSVData; int main(void) { // create a simple pool for various different objects CxMempool* pool = cxMempoolCreateSimple(128); FILE *f = fopen("test.csv", "r"); if (f == NULL) { perror("Cannot open file"); return 1; } // close the file automatically at pool destruction cxMempoolRegister(pool, f, (cx_destructor_func) fclose); // create a buffer using the memory pool for destruction CxBuffer *content = cxBufferCreate( NULL, 256, pool->allocator, CX_BUFFER_AUTO_EXTEND ); // read the file into the buffer and turn it into a string cx_stream_copy( f, content, (cx_read_func) fread, cxBufferWriteFunc ); fclose(f); cxstring contentstr = cx_strn(content->space, content->size); // split the string into lines // use the memory pool to allocate the target array cxstring* lines; size_t lc = cx_strsplit_a( pool->allocator, contentstr, cx_str("\n"), SIZE_MAX, &lines ); // skip the header and parse the remaining data into a linked list // the nodes of the list shall also be allocated by the pool CxList* datalist = cxLinkedListCreate( pool->allocator, NULL, sizeof(CSVData) ); for (size_t i = 1 ; i < lc ; i++) { if (lines[i].length == 0) continue; cxstring fields[3]; size_t fc = cx_strsplit(lines[i], cx_str(";"), 3, fields); if (fc != 3) { fprintf(stderr, "Syntax error in line %zu.\n", i); cxMempoolFree(pool); return 1; } CSVData data; data.column_a = fields[0]; data.column_b = fields[1]; data.column_c = fields[2]; cxListAdd(datalist, &data); } // iterate through the list and output the data CxIterator iter = cxListIterator(datalist); cx_foreach(CSVData*, data, iter) { printf("Column A: %.*s | " "Column B: %.*s | " "Column C: %.*s\n", (int)data->column_a.length, data->column_a.ptr, (int)data->column_b.length, data->column_b.ptr, (int)data->column_c.length, data->column_c.ptr ); } // cleanup everything, no manual free() needed cxMempoolFree(pool); return 0; } ``` <seealso> <category ref="apidoc"> <a href="https://ucx.sourceforge.io/api/mempool_8h.html">mempool.h</a> </category> </seealso>