# How to Migrate Existing Code from malloc to mulle_allocator: 3 Incremental Steps

> Easily migrate your code from malloc to mulle_allocator in three simple steps. Swap C functions, use custom allocators, and embed pointers for complete control with mulle_allocator.

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

---

**You can migrate existing code from malloc to mulle_allocator in three incremental steps by first swapping standard C functions for drop-in wrappers like `mulle_malloc`, then introducing explicit custom allocators where needed, and finally embedding allocator pointers inside your data structures for full control.**

The **mulle-c/mulle-allocator** library provides a flexible, drop-in replacement for C standard library memory allocation that maintains API compatibility while adding custom error handling and memory tracking capabilities. Learning how to migrate existing code from malloc to mulle_allocator allows you to introduce per-module allocation strategies and test-time leak detection without breaking existing functionality. This guide walks through a practical migration path using the actual source implementation found in [`src/mulle-allocator.h`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator.h).

## Step 1: Replace malloc with Global mulle_allocator Wrappers

The first migration phase replaces direct calls to `malloc`, `calloc`, `realloc`, and `free` with the convenience wrappers provided in [`src/mulle-allocator.h`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator.h). These wrappers—`mulle_malloc`, `mulle_calloc`, `mulle_realloc`, `mulle_free`, and `mulle_strdup`—maintain identical function signatures to their standard library counterparts.

Because these functions use the **global default allocator** internally, no structural code changes are required. The wrappers abort on allocation failure by default, allowing you to remove repetitive NULL-checking error handling.

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

/* Before */
char *buf = malloc(1024);
if (!buf) {
    perror("malloc");
    exit(1);
}

/* After */
char *buf = mulle_malloc(1024);  // Aborts on OOM, no check needed
/* ... use buf ... */
mulle_free(buf);

```

## Step 2: Introduce Custom mulle_allocator Instances

Once the global wrappers are in place, introduce **custom allocator** instances where you need specialized allocation behavior, such as test-time leak detection or shared memory pools. Create a `struct mulle_allocator` instance and pass it explicitly to the `mulle_allocator_*` family of functions.

This approach enables per-module or per-object memory control without modifying unrelated code paths. As defined in [`src/mulle-allocator.h`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator.h), you can initialize a custom allocator from the standard library baseline or configure custom failure handlers.

```c
/* Create a custom allocator using standard library functions */
struct mulle_allocator myalloc = mulle_allocator_stdlib;

/* Optional: set a custom failure handler */
mulle_allocator_set_fail(&myalloc, my_fail_handler);

/* Explicit allocation with custom allocator */
char *msg = mulle_allocator_malloc(&myalloc, 128);
strcpy(msg, "Hello, world!");
mulle_allocator_free(&myalloc, msg);

```

## Step 3: Embed mulle_allocator in Data Structures

For complete allocator control, **embed the allocator pointer** inside your data structures. Store a `struct mulle_allocator *` field within your structs and always use that pointer for subsequent allocations and deallocations of the object and its members.

This pattern eliminates the risk of mixing allocators and makes your API allocator-aware, enabling test-time swapping by simply passing a different allocator during object creation.

```c
struct buffer {
    struct mulle_allocator *allocator;
    size_t   size;
    void    *data;
};

struct buffer *buffer_create(size_t sz, struct mulle_allocator *alloc)
{
    struct buffer *b = mulle_allocator_malloc(
        alloc ? alloc : &mulle_allocator_default, 
        sizeof *b
    );
    b->allocator = alloc ? alloc : &mulle_allocator_default;
    b->size = sz;
    b->data = mulle_allocator_malloc(b->allocator, sz);
    return b;
}

static inline void buffer_destroy(struct buffer *b)
{
    mulle_allocator_free(b->allocator, b->data);
    mulle_allocator_free(b->allocator, b);
}

```

### Optional: Enable ABA-Safe Free Operations

For lock-free data structures requiring address-based allocation safety, configure the **ABA pointer** once per allocator. Set the ABA context and free function using `mulle_allocator_set_aba`, then use `mulle_allocator_abafree` instead of the standard free function.

```c
mulle_allocator_set_aba(&myalloc, my_aba_context, my_aba_free_function);

/* Later, in lock-free code paths */
mulle_allocator_abafree(&myalloc, block);

```

## Summary

- **Start with drop-in wrappers**—Replace `malloc`, `calloc`, `realloc`, and `free` with `mulle_malloc`, `mulle_calloc`, `mulle_realloc`, and `mulle_free` to use the global default allocator without structural changes.
- **Introduce custom allocators**—Create `struct mulle_allocator` instances and use `mulle_allocator_malloc` and `mulle_allocator_free` for per-module memory control.
- **Embed for full control**—Store allocator pointers inside data structures to prevent mixing allocators and enable test-time instrumentation.
- **Leverage built-in safety**—The default allocator aborts on out-of-memory conditions, eliminating the need for manual NULL checks throughout your codebase.

## Frequently Asked Questions

### Can I mix mulle_allocator functions with standard malloc/free during migration?

Yes. The **global default allocator** used by `mulle_malloc` and related wrappers ultimately delegates to the standard C library `malloc` and `free` implementations. You can freely intermix standard library calls with mulle_allocator wrappers during incremental migration, though you should eventually standardize on one approach per module to avoid confusion.

### How does mulle_allocator handle out-of-memory errors differently than malloc?

Unlike standard `malloc`, which returns `NULL` on allocation failure, the **default allocator** in mulle-c/mulle-allocator aborts the program immediately on out-of-memory conditions through its configured *fail* vector. This design eliminates the need for repetitive `if (!ptr)` error handling. You can customize this behavior per allocator using `mulle_allocator_set_fail` to install your own failure handler.

### What is the performance overhead of using mulle_allocator wrappers?

The overhead is **negligible**. The convenience wrappers like `mulle_malloc` are inline functions that forward directly to the underlying allocator's function pointers. When using the global default allocator configured for standard library allocation, the code path is essentially equivalent to calling `malloc` directly, as implemented in [`src/mulle-allocator.h`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator.h).

### When should I use ABA-safe free operations?

Use **ABA-safe free** (`mulle_allocator_abafree`) when implementing lock-free data structures where memory addresses might be reused, creating ABA problems. Configure the ABA context once per allocator using `mulle_allocator_set_aba`. This ensures that freed memory is handled through your custom deferred-free mechanism rather than being immediately returned to the allocator, preventing race conditions in concurrent code.