--- a/src/cx/mempool.h Thu May 22 16:23:55 2025 +0200 +++ b/src/cx/mempool.h Thu May 22 16:25:32 2025 +0200 @@ -43,8 +43,52 @@ extern "C" { #endif -/** Internal structure for pooled memory. */ -struct cx_mempool_memory_s; +struct cx_mempool_memory_s { + /** The destructor. */ + cx_destructor_func destructor; + /** The actual memory. */ + char c[]; +}; + +struct cx_mempool_memory2_s { + /** The destructor. */ + cx_destructor_func2 destructor; + /** Data for the destructor. */ + void *data; + /** The actual memory. */ + char c[]; +}; + +struct cx_mempool_foreign_memory_s { + /** The foreign memory. */ + void* mem; + union { + /** Simple destructor. */ + cx_destructor_func destr; + /** Advanced destructor. */ + cx_destructor_func2 destr2; + }; + /** Data for the advanced destructor. */ + void *destr2_data; +}; + +/** Specifies how individual blocks are allocated. */ +enum cx_mempool_type { + /** + * Allows registration of cx_destructor_func for each memory block. + */ + CX_MEMPOOL_TYPE_SIMPLE, + /** + * Allows registration of cx_destructor_func2 for each memory block. + */ + CX_MEMPOOL_TYPE_ADVANCED, + /** + * No individual destructor registration allowed. + * + * In this mode, no additional memory per block is allocated. + */ + CX_MEMPOOL_TYPE_PURE, +}; /** * The basic structure of a memory pool. @@ -54,20 +98,46 @@ /** The provided allocator. */ const CxAllocator *allocator; - /** - * A destructor that shall be automatically registered for newly allocated memory. - * This destructor MUST NOT free the memory. - */ - cx_destructor_func auto_destr; - /** Array of pooled memory. */ - struct cx_mempool_memory_s **data; + void **data; /** Number of pooled memory items. */ size_t size; /** Memory pool capacity. */ size_t capacity; + + /** Array of registered memory. */ + struct cx_mempool_foreign_memory_s *registered; + + /** Number of registered memory items. */ + size_t registered_size; + + /** Capacity for registered memory. */ + size_t registered_capacity; + + /** + * A destructor that shall be called before deallocating a memory block. + * This destructor MUST NOT free the memory itself. + * + * It is guaranteed that this destructor is called after the individual + * destructor of the memory block and before @c destr2. + */ + cx_destructor_func destr; + + /** + * A destructor that shall be called before deallocating a memory block. + * This destructor MUST NOT free the memory itself. + * + * It is guaranteed that this destructor is called after the individual + * destructor of the memory block and @c destr. + */ + cx_destructor_func2 destr2; + + /** + * Additional data for the @c destr2. + */ + void *destr2_data; }; /** @@ -84,19 +154,39 @@ void cxMempoolFree(CxMempool *pool); /** - * Creates an array-based memory pool with a shared destructor function. + * Creates an array-based memory pool. * - * This destructor MUST NOT free the memory. + * The type determines how much additional memory is allocated per block + * to register a destructor function. * - * @param capacity the initial capacity of the pool - * @param destr optional destructor function to use for allocated memory + * @param capacity the initial capacity of the pool (an implementation default if zero) + * @param type the type of memory pool * @return the created memory pool or @c NULL if allocation failed */ cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxMempoolFree, 1) cx_attr_export -CxMempool *cxMempoolCreate(size_t capacity, cx_destructor_func destr); +CxMempool *cxMempoolCreate(size_t capacity, enum cx_mempool_type type); + +/** + * Sets the global destructor for all memory blocks within the specified pool. + * + * @param pool (@c CxMempool*) the memory pool + * @param fnc (@c cx_destructor_func) the destructor that shall be applied to all memory blocks + */ +#define cxMempoolGlobalDestructor(pool, fnc) \ + (pool)->destr = (cx_destructor_func)(fnc) + +/** + * Sets the global destructor for all memory blocks within the specified pool. + * + * @param pool (@c CxMempool*) the memory pool + * @param fnc (@c cx_destructor_func2) the destructor that shall be applied to all memory blocks + * @param data (@c void*) additional data for the destructor function + */ +#define cxMempoolGlobalDestructor2(pool, fnc, data) \ + (pool)->destr2 = (cx_destructor_func2)(fnc); (pool)->destr2_data = (data) /** * Creates a basic array-based memory pool. @@ -104,11 +194,12 @@ * @param capacity (@c size_t) the initial capacity of the pool * @return (@c CxMempool*) the created memory pool or @c NULL if allocation failed */ -#define cxMempoolCreateSimple(capacity) cxMempoolCreate(capacity, NULL) +#define cxMempoolCreateSimple(capacity) cxMempoolCreate(capacity, CX_MEMPOOL_TYPE_SIMPLE) /** * Sets the destructor function for a specific allocated memory object. * + * If the type of memory pool is not #CX_MEMPOOL_TYPE_SIMPLE, the behavior is undefined. * If the memory is not managed by a UCX memory pool, the behavior is undefined. * The destructor MUST NOT free the memory. * @@ -123,10 +214,29 @@ ); /** + * Sets the destructor function for a specific allocated memory object. + * + * If the type of memory pool is not #CX_MEMPOOL_TYPE_ADVANCED, the behavior is undefined. + * If the memory is not managed by a UCX memory pool, the behavior is undefined. + * The destructor MUST NOT free the memory. + * + * @param memory the object allocated in the pool + * @param fnc the destructor function + * @param data additional data for the destructor function + */ +cx_attr_nonnull +cx_attr_export +void cxMempoolSetDestructor2( + void *memory, + cx_destructor_func2 fnc, + void *data +); + +/** * Removes the destructor function for a specific allocated memory object. * + * If the type of memory pool is not #CX_MEMPOOL_TYPE_SIMPLE, the behavior is undefined. * If the memory is not managed by a UCX memory pool, the behavior is undefined. - * The destructor MUST NOT free the memory. * * @param memory the object allocated in the pool */ @@ -135,12 +245,25 @@ void cxMempoolRemoveDestructor(void *memory); /** + * Removes the destructor function for a specific allocated memory object. + * + * If the type of memory pool is not #CX_MEMPOOL_TYPE_ADVANCED, the behavior is undefined. + * If the memory is not managed by a UCX memory pool, the behavior is undefined. + * + * @param memory the object allocated in the pool + */ +cx_attr_nonnull +cx_attr_export +void cxMempoolRemoveDestructor2(void *memory); + +/** * Registers foreign memory with this pool. * * The destructor, in contrast to memory allocated by the pool, MUST free the memory. + * This function can be used with any pool of any type, since destructors for registered memory + * are entirely independent of the pool's memory management. * - * A small portion of memory will be allocated to register the information in the pool. - * If that allocation fails, this function will return non-zero. + * The destructor for the registered memory will be called after all pooled items have been freed. * * @param pool the pool * @param memory the object to register (MUST NOT be already allocated in the pool) @@ -156,6 +279,35 @@ cx_destructor_func destr ); + +/** + * Registers foreign memory with this pool. + * + * The destructor, in contrast to memory allocated by the pool, MUST free the memory. + * This function can be used with any pool of any type, since destructors for registered memory + * are entirely independent of the pool's memory management. + * + * The destructor for the registered memory will be called after all pooled items have been freed. + * + * @attention The data pointer MUST NOT be @c NULL. + * If you wish to register a destructor without additional data, use cxMempoolRegister(). + * + * @param pool the pool + * @param memory the object to register (MUST NOT be already allocated in the pool) + * @param destr the destructor function + * @param data additional data for the destructor function + * @retval zero success + * @retval non-zero failure + */ +cx_attr_nonnull +cx_attr_export +int cxMempoolRegister2( + CxMempool *pool, + void *memory, + cx_destructor_func2 destr, + void *data +); + /** * Transfers all the memory managed by one pool to another. * @@ -164,10 +316,12 @@ * * The source pool will get a completely new allocator and can be reused or destroyed afterward. * + * This function fails when the destination pool has a different type than the source pool. + * * @param source the pool to move the memory from * @param dest the pool where to transfer the memory to * @retval zero success - * @retval non-zero failure + * @retval non-zero allocation failure or incompatible pools */ cx_attr_nonnull cx_attr_export @@ -179,8 +333,7 @@ /** * Transfers an object from one pool to another. * - * You can only transfer objects that have been allocated by this pool. - * Objects that have been registered with cxMempoolRegister() cannot be transferred this way. + * This function fails when the destination pool has a different type than the source pool. * * @attention If the object maintains a reference to the pool's allocator, * you must make sure to update that reference to the allocator of the destination pool. @@ -189,7 +342,7 @@ * @param dest the pool where to transfer the memory to * @param obj pointer to the object that shall be transferred * @retval zero success - * @retval non-zero failure or object was not found in the source pool + * @retval non-zero failure, or the object was not found in the source pool, or the pools are incompatible */ cx_attr_nonnull cx_attr_export