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. For example, the UCX string functions provide several variants suffixed with _a for that purpose.
Basic Memory Management
A memory pool is created with the cxMempoolCreate() family of functions with a default capacity. If capacity is set to zero, an implementation default is used.
The type specifies how much additional data is allocated for each pooled memory block. A simple pool reserves memory for an optional cx_destructor_func. An advanced pool reserves memory for an optional cx_destructor_func2 and an additional data pointer that will be passed to that destructor. A pure pool does not reserve any additional data and therefore does not support registering custom destructors with the allocated memory.
The functions cxMempoolGlobalDestructor() and cxMempoolGlobalDestructor2() can be used to specify destructor functions that shall be invoked for all objects allocated by the pool when they are freed (see Order of Destruction). This is usually only useful for pools that will only contain objects of the same type.
In simple memory pools, the two functions cxMempoolSetDestructor() and cxMempoolRemoveDestructor() can be used to assign a specific destructor function to an allocated object or remove an assigned destructor function, respectively. The memory pointer points to the allocated object, which must have been allocated by any CxMempool's provided allocator. For advanced pools, the functions cxMempoolSetDestructor2() and cxMempoolRemoveDestructor2() do the same. It is disallowed to use the functions with a pool of the wrong type and will most likely cause undefined behavior. Pure pools do not allow setting destructors for individual memory blocks at all.
The cxMempoolRegister() function allocates a new wrapper object for memory and makes the specified destructor function being called when the pool gets destroyed. Alternatively, the cxMempoolRegister2() function can be used to register an advanced destructor and a pointer to custom data. Be aware that the memory pointed to by the additional data pointer must remain valid until the pool gets destroyed! Usually these functions return 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:
In any order, for each object allocated by the pool
the destructor function assigned to that object is called
the pool's global simple destructor is called
the pool's global advanced destructor is called
the object's memory is deallocated
In any order, for each registered foreign object the destructor is called
The pool memory is deallocated
The pool structure is deallocated
Transfer Memory
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 cxMempoolTransferObject() transfers a single object managed by the source pool to the dest pool. In contrast to transferring an entire pool, if obj has a reference to source->allocator, it must be updated to dest->allocator manually. It is also possible to transfer registered memory from one pool to another, this way.
The functions return zero when the transfer was successful, and non-zero under one of the following error conditions:
a necessary memory allocation was not possible
the
sourceanddestpointers point to the same poolthe pools have a different type (simple, advanced, pure)
the pools have different base allocators (i.e.
cxDefaultAllocatorchanged between creation of the pools)
In case of an error, no memory is transferred and both pools remain unchanged and are in a valid state.
Example
The following code illustrates how the contents of a CSV file are read into pooled memory.