Next: , Previous: Feature checking, Up: Top


7 Memory Allocation

The ANSI C library provides functions which allow the programmer to dynamically allocate memory, and to manipulate this memory through pointers. Specifically, the ‘malloc(n)’ call allocates a block of n bytes and returns a pointer to it. But if the memory could not be allocated, ‘malloc’ returns a null pointer. The programmer is required to check the return value of ‘malloc’ for validity whenever doing dynamic allocation.

Now, in many programs, the only sensible action to be taken on being unable to allocate the required memory is exiting. So, Libretto provides wrappers around ‘malloc’ and related functions.1 Normally, these wrappers will quit the program if they fail to perform a requested allocation.

Of course, users should also be able to alter the behaviour in out-of-memory conditions. Patterns of desired behaviour are communicated between the library and its users as values of type ‘enum mem_oom_mode’.2 The out-of-memory behaviour will affect not only the ‘mem_alloc’ functions themselves, but also any library function that allocates memory. The values allowed by this type are as follows:

LIBRETTO_OOM_QUIT
On out-of-memory conditions, print an appropriate error message and quit as if ‘msg_fatal’ was called.
LIBRETTO_OOM_SEGV
On out-of-memory conditions, print an appropriate error message, and then attempt to force a `segmentation violation' (‘SIGSEGV’), hopefully generating a core dump. If the segfault fails, exit by calling ‘abort()’.
LIBRETTO_OOM_RETURN
On out-of-memory conditions, return an appropriate error value. The allocation functions themselves return ‘NULL’, as do other functions which attempt to create blocks of memory. Many functions return a success/failure code of type ‘int’, where 0 indicates success and -1 indicates failure. Other functions return ‘EOF’ on error; if the error was due to an out-of-memory condition, then the global libc variable ‘errno’ is set to ‘ENOMEM

Note that other behaviours may be introduced in the future.

— Function: enum mem_oom_mode mem_failure_mode (void)

Returns the current behaviour for out-of-memory conditions.

— Function: int mem_set_failure_mode (enum mem_oom_mode mode)

Sets the current behaviour for out-of-memory conditions to mode. mode must be one of the values listed above. Returns 0 on success and -1 on failure.

Note that calling this function alters data that is global to each process, though using it once at the start of ‘main’ should not have a detrimental effect on multi-threaded applications.

For the sake of brevity and clarity, the remaining chapters in this manual are written as if the current failure mode is ‘LIBRETTO_OOM_RETURN’.

The allocation functions are as follows:

— Function: void * mem_alloc (size_t size)

This function calls ‘malloc’ to allocate a block of memory size bytes long. If that call succeeds, ‘mem_alloc’ returns the address of the block. Otherwise, it will print a message to stderr and exit the program.

— Function: void * mem_realloc (void *ptr, size_t size)

This function calls ‘realloc’ to change the size of the allocated block starting at ptr to be size bytes long, copying data as needed. Passing a null pointer as the value of ptr causes a new block to be allocated, and passing 0 as the value of size causes the block to be freed. It returns a pointer to the new block on success, and fails in the same way as ‘mem_alloc’ otherwise. The behaviour is undefined if ptr was not allocated by one of the ‘mem_alloc’ functions and is not null.

— Function: int mem_try_realloc (void **ptr, size_t size)

This function attempts to call ‘realloc’ to change the size of the allocated block starting at *ptr to be size bytes long, copying data as needed. Passing a null pointer as *ptr causes ‘mem_try_realloc’ to try to allocate a fresh block. Passing 0 as the value of size causes the block described by *ptr to be freed.

mem_try_realloc’ returns 0 if successful, and -1 otherwise. If it successfully allocated a block, it sets *ptr to the address of the new block. If it freed a block (which is always successful), it sets *ptr to the null pointer. Otherwise, it leaves *ptr unchanged.

Note that this function will never quit if it failed to allocate memory – it is not affected by the current value of ‘mem_failure_mode()’. It is intended for `harmless reallocations': cases where the programmer knows that any reallocation will in fact shrink the allocated block. It is used for that purpose in several places in Libretto itself.

Note also that the C language has no `generic pointer to pointer' type. This means that the correct calling procedure for ‘mem_try_realloc’ is as follows:

          void *generic_pointer = object_pointer;
          i = mem_try_realloc (&generic_pointer, new_size);
          object_pointer = generic_pointer;

However, for most machines (those which have a uniform representation for all object pointers) this roundabout technique is unnecessary.

— Function: void mem_free (void *ptr)

This function frees a previously allocated block of memory. If ptr is null, no action is performed. The behaviour is undefined if ptr was not allocated by one of the ‘mem_alloc’ functions and is not null.


Footnotes

[1] Libretto guarantees that the pointers manipulated by these functions are received and passed to ‘malloc’, ‘realloc’ and ‘free’ unchanged.

[2] The `oom' portion stands for `out-of-memory'.