# How to Implement a Custom Fail Handler in mulle-allocator That Logs and Continues

> Implement a custom fail handler in mulle-allocator to log errors and continue execution. Learn how to easily set up your own handler for robust memory management.

- Repository: [mulle-c/mulle-allocator](https://github.com/mulle-c/mulle-allocator)
- Tags: how-to-guide
- Published: 2026-03-07

---

**To implement a custom fail handler that logs and continues in mulle-allocator, define a function matching the `mulle_allocator_fail_t` signature that logs the error and returns normally, then install it using `mulle_allocator_set_fail()`.**

The `mulle-c/mulle-allocator` library provides a flexible memory allocation framework for C applications. By default, when allocation fails, the library terminates the program via `abort()`. However, many production environments require a **custom fail handler that logs and continues** execution, allowing applications to degrade gracefully rather than crashing immediately.

## Understanding the Default Allocation Failure Behavior

In [`src/mulle-allocator.c`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator.c) at lines 44–51, the default handler `mulle_allocation_fail` prints an error message using `perror` and immediately invokes `abort()`. This function carries the `_MULLE_C_NO_RETURN` attribute, signaling to the compiler that control never returns to the caller.

The failure handler is invoked within every allocation wrapper, including `_mulle_allocator_malloc`, `_mulle_allocator_calloc`, `_mulle_allocator_realloc`, `_mulle_allocator_realloc_strict`, and `_mulle_allocator_strdup`. When the default handler executes, the program terminates unconditionally, making it unsuitable for scenarios requiring error recovery or detailed logging.

## Designing a Custom Fail Handler That Logs and Continues

To override the abort behavior, you must provide a replacement function conforming to the `mulle_allocator_fail_t` type defined in [`src/mulle-allocator.h`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator.h) at lines 175–178.

### The Required Function Signature

Your custom handler must match this exact signature:

```c
typedef void mulle_allocator_fail_t( struct mulle_allocator *allocator,
                                     void *block,
                                     size_t size );

```

Unlike the default implementation, your function should **not** be marked with `_MULLE_C_NO_RETURN`. This attribute omission is critical; it allows the compiler to generate code for the normal return path, enabling the allocation wrapper to return `NULL` to its caller instead of terminating.

### Installing Your Handler

Use the inline helper `mulle_allocator_set_fail`, defined in [`src/mulle-allocator.h`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator.h) at lines 187–194, to register your handler:

```c
static inline void mulle_allocator_set_fail( struct mulle_allocator *allocator,
                                             mulle_allocator_fail_t *fail);

```

Pass a pointer to your allocator instance, or `NULL` to modify the global default allocator. Passing `NULL` as the second argument restores the default aborting behavior.

## Practical Implementation Examples

### Example 1: Instance-Specific Logger

This implementation creates a custom allocator based on the standard library allocator but replaces the failure handler to log and continue:

```c
#include <mulle-allocator/mulle-allocator.h>
#include <stdio.h>

static void my_fail_handler( struct mulle_allocator *allocator,
                             void *block,
                             size_t size)
{
    fprintf( stderr,
             "mulle-allocator: allocation of %zu bytes failed (allocator %p)\n",
             size,
             (void *) allocator );
    
    /* Return normally without aborting - execution continues */
    (void) block;
    (void) allocator;
}

int main( void)
{
    struct mulle_allocator my_allocator = mulle_allocator_stdlib;
    mulle_allocator_set_fail( &my_allocator, my_fail_handler );

    /* Force a failure by requesting an impossible amount of memory */
    void *p = mulle_allocator_malloc( &my_allocator, (size_t) -1 );
    
    if( !p)
        printf( "Allocation returned NULL as expected.\n" );

    return 0;
}

```

### Example 2: Global Default Handler

To affect all code using the default allocator without explicit instance passing, install a handler on the global allocator:

```c
#include <mulle-allocator/mulle-allocator.h>
#include <stdio.h>

static void global_fail_logger( struct mulle_allocator *allocator,
                                void *block,
                                size_t size)
{
    fprintf( stderr,
             "[global] allocation of %zu bytes failed (allocator %p)\n",
             size, (void *) allocator );
}

__attribute__((constructor))
static void install_global_logger(void)
{
    /* NULL selects the global default allocator */
    mulle_allocator_set_fail( NULL, global_fail_logger );
}

```

The `constructor` attribute ensures registration occurs automatically before `main()` executes.

### Example 3: Test Suite Reference

The file [`test/coverage/fail-stuff.c`](https://github.com/mulle-c/mulle-allocator/blob/main/test/coverage/fail-stuff.c) provides a minimal working example at lines 33–37. The custom handler `fail_fail` prints the allocation size and returns normally, demonstrating exactly how to implement a **custom fail handler that logs and continues** without terminating the application. This serves as the canonical reference for handler implementation patterns.

## Handling NULL Returns in Application Code

When your custom handler returns rather than aborts, the allocation wrapper returns `NULL` to the caller. Your application code must check for these `NULL` pointers exactly as you would with standard `malloc` calls. This pattern maintains consistency with C convention while adding robust logging capabilities and allowing the program to attempt recovery or shutdown procedures.

## Summary

- The default `mulle_allocation_fail` in [`src/mulle-allocator.c`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator.c) terminates the program via `abort()`.
- Define a function matching `mulle_allocator_fail_t` from [`src/mulle-allocator.h`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator.h) that logs and returns normally to enable continuation.
- Install the handler using `mulle_allocator_set_fail`, passing `NULL` to modify the global allocator or a specific instance pointer.
- Ensure your application checks for `NULL` returns from allocation calls when using non-aborting handlers.
- Reference [`test/coverage/fail-stuff.c`](https://github.com/mulle-c/mulle-allocator/blob/main/test/coverage/fail-stuff.c) for a concrete implementation example of logging without termination.

## Frequently Asked Questions

### What happens if my custom fail handler returns instead of aborting?

When your handler returns, the allocation wrapper (such as `_mulle_allocator_malloc`) returns `NULL` to the caller. The program continues execution, and you must handle the `NULL` pointer appropriately in your application logic to prevent dereferencing errors.

### Can I use different fail handlers for different allocators?

Yes. The `fail` member belongs to individual `struct mulle_allocator` instances, as shown in [`src/mulle-allocator-struct.h`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator-struct.h). You can create multiple allocator instances with distinct handlers using `mulle_allocator_set_fail`, allowing different logging strategies or failure behaviors for different subsystems or modules.

### How do I restore the default aborting behavior?

Call `mulle_allocator_set_fail` with `NULL` as the second argument (the handler pointer). This restores the default `mulle_allocation_fail` function that prints to stderr and calls `abort()`, effectively reverting to the library's standard failure mode.

### Is the fail handler called for every allocation failure?

Yes. According to the source code in [`src/mulle-allocator.h`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator.h), the handler is invoked inside every allocation wrapper including `_mulle_allocator_malloc`, `_mulle_allocator_calloc`, `_mulle_allocator_realloc`, `_mulle_allocator_realloc_strict`, and `_mulle_allocator_strdup` whenever the underlying system allocation returns `NULL`.