mulle-allocator
๐ Flexible C memory allocation scheme
... provides a way to pass around the memory scheme du jour
... has identical API to malloc, realloc, free
... frees your code from having to check for errors when allocating memory
mulle-allocator has a companion project: mulle-testallocator. mulle-testallocator provides the error and leak detection, that was formerly a part of mulle-allocator..
Release Version |
---|
mulle_malloc
instead of malloc
to reduce code size
Use Instead of:
if( ! malloc( 1848))
{
perror( "malloc:");
exit( 1);
}
if( ! calloc( 18, 48))
{
perror( "calloc:");
exit( 1);
}
s = strdup( "VfL Bochum 1848");
if( ! s)
{
perror( "strdup:");
exit( 1);
}
if( ! realloc( s, 18);
{
perror( "realloc:");
exit( 1);
}
free( s);
write
mulle_malloc( 1848);
mulle_calloc( 18, 48);
s = mulle_strdup( "VfL Bochum 1848");
mulle_realloc( s, 18);
mulle_free( s);
You don't have to check for out of memory error conditions anymore. Otherwise your code will run the same and a possible performance degradation because of the indirection will be hardly measurable.
How mulle-allocator deals with memory shortage
By the C standard malloc
returns NULL and sets errno
to ENOMEM
, if
it can't satisfy the memory request. Here are the two most likely scenarios why
this happens:
The caller specified a huge amount of memory, that the OS can't give you. Typically (size_t) -1 can never work. This is considered to be a bug on the callers part.
Or the program has exhausted all memory space available to the process. Here is what happens on various OS:
OS | malloc fate |
---|---|
FreeBSD | malloc hangs, probably waiting for memory to become available |
MacOS X | malloc slowly brings the system to a crawl, no error observed |
Linux | malloc returns an error |
Windows | unknown |
The gist is, that in portable programs it doesn't really make sense to rely
on malloc
returning NULL and doing something clever based on it.
If we define the mulle-allocator malloc to always return a valid memory block - discounting erroneous parameters as programmers error, to be caught during development - then memory calling code simplifies from:
p = malloc( size);
if( ! p)
return( -1);
memcpy( p, q, size);
to
p = mulle_malloc( size);
memcpy( p, q, size);
mulle_allocator
to make your code more flexible
Use You can make your code, and especially your data structures, more flexible by
using a mulle_allocator
. This decouples your data structure from stdlib.
It enables your data structure to reside in shared or wired memory
with no additional code. Your API consumers just have to pass their own
allocators.
Also it can be helpful to isolate your data structure memory allocation during tests. This way, other, possibly benign, code leaks, do not obscure the test.
What is an allocator ?
The mulle_allocator
struct is a collection of function pointers, with one
added pointer for aba
and looks like this:
struct mulle_allocator
{
void *(*calloc)( size_t n, size_t size, struct mulle_allocator *p);
void *(*realloc)( void *block, size_t size, struct mulle_allocator *p);
void (*free)( void *block, struct mulle_allocator *p);
void (*fail)( struct mulle_allocator *p, void *block, size_t size) MULLE_C_NO_RETURN;
int (*abafree)( void *aba, void (*free)( void *), void *block);
void *aba;
};
By default
.aba
and.abafree
are not available. If you need ABA safe freeing, it is recommended to use mulle-aba.
You should not jump through the vectors directly, but use
supplied inline functions like mulle_allocator_malloc
, as they perform the
necessary return value checks (see below: Dealing with memory shortage).
The shortcut functions mulle_malloc
use the mulle_default_allocator
by default and save you some typework. You can also use NULL
as the
allocator for mulle_allocator_malloc
with the same effect of choosing
the mulle_default_allocator
.
Embedding the allocator in your data structure
A pointer to the allocator could be kept in your data structure. This simplifies your API, as the allocator is only needed during creation. Here is an example how to use the allocator in this fashion:
struct my_string
{
struct mulle_allocator *allocator;
char s[ 1];
};
struct my_string *my_string_alloc( char *s, struct mulle_allocator *allocator)
{
size_t len;
struct my_string *p;
len = s ? strlen( s) : 0;
p = mulle_allocator_malloc( allocator, sizeof( struct my_string) + len);
dst->allocator = allocator;
memcpy( p->s, s, len);
p->s[ len] = 0;
return( p);
}
static inline void my_string_free( struct my_string *p)
{
mulle_allocator_free( p->allocator, p);
}
Not embedding the allocator in your data structure
But if you don't want to store the allocator inside the data structure, you can pass it in again:
struct my_other_string
{
char s[ 1];
};
struct my_other_string *my_other_string_alloc( char *s, struct mulle_allocator *allocator)
{
size_t len;
struct my_other_string *p;
len = s ? strlen( s) : 0;
p = mulle_allocator_malloc( allocator, sizeof( struct my_other_string) + len);
memcpy( p->s, s, len);
p->s[ len] = 0;
return( p);
}
static inline void my_other_string_free( struct my_other_string *p,
struct mulle_allocator *allocator)
{
mulle_allocator_free( allocator, p);
}
The disadvantage is, that this opens the door for bugs, as you may be using different allocators accidentally.
Caveats
The mulle_default_allocator
and mulle_stdlib_allocator
never return, if the
allocation went bad. If an allocator function detects, that an allocation can
not be satisfied, it jumps through its fail vector. This will print an error
message and exit the program.
You can not pass a zero size to either mulle_realloc
or mulle_malloc
without getting a failure. If you want to free memory with realloc - by
passing a zero block size - you need to use mulle_realloc_strict
.
If you pass a zero block size and a zero block to mulle_realloc_strict
, it
will return NULL.
API
You are here
Add
Use mulle-sde to add mulle-allocator to your project:
mulle-sde dependency add --c --github mulle-c mulle-allocator
Install
mulle-sde
Use mulle-sde to build and install mulle-allocator and all dependencies:
mulle-sde install --prefix /usr/local \
https://github.com/mulle-c/mulle-allocator/archive/latest.tar.gz
Manual Installation
Install the requirements:
Requirements | Description |
---|---|
mulle-c11 | Compiler glue |
Install into /usr/local
:
cmake -B build \
-DCMAKE_INSTALL_PREFIX=/usr/local \
-DCMAKE_PREFIX_PATH=/usr/local \
-DCMAKE_BUILD_TYPE=Release &&
cmake --build build --config Release &&
cmake --install build --config Release
Author
Nat! for Mulle kybernetiK and Codeon GmbH
Platforms and Compilers
All platforms and compilers supported by mulle-c11.