# Using mulle_allocator with Memory-Mapped Files or Shared Memory

> Learn how to use mulle_allocator with memory-mapped files or shared memory by providing custom function pointers that wrap system calls like mmap. Discover the flexibility of mulle_allocator for advanced memory management.

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

---

**Yes, you can use mulle_allocator with memory-mapped files or shared memory by supplying custom function pointers that wrap `mmap`, `shm_open`, or similar system calls.**

The `mulle-c/mulle-allocator` library provides a thin, configurable abstraction over raw memory operations through the `struct mulle_allocator` type. Because the library delegates all allocation work to function pointers stored in this structure, you can seamlessly replace heap-based allocation with memory-mapped files or shared memory segments without modifying your application logic.

## How mulle_allocator Supports Custom Memory Sources

At its core, `mulle_allocator` is a dispatch structure defined in [`src/mulle-allocator-struct.h`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator-struct.h) (lines 53‑60). The struct holds five function pointers: `calloc`, `realloc`, `free`, `fail`, and `abafree`. When you call `mulle_allocator_malloc()`, the library internally invokes `_mulle_allocator_calloc()` from [`src/mulle-allocator.h`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator.h) (lines 27‑31), which simply dereferences these pointers.

The default implementation, `mulle_allocator_default` defined in [`src/mulle-allocator.c`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator.c) (lines 22‑32), forwards to the standard C library (`calloc`, `realloc`, `free`). However, because no internal bookkeeping assumes a specific allocation mechanism, any pointer returned by your custom functions is treated as valid memory until you hand it back to the matching `free` or `realloc` callbacks.

## Implementing a Memory-Mapped File Allocator

To integrate `mmap` or shared memory, you create a custom allocator instance by following three steps.

### Step 1: Define Custom Allocation Functions

Your functions must match the signatures declared in the struct. The `fail` and `abafree` callbacks can usually remain as the defaults (`mulle_allocation_fail` and `mulle_allocator_no_aba_abort`) unless you need special error handling.

```c
void *my_mmap_calloc(size_t n, size_t size, struct mulle_allocator *alloc);
void *my_mmap_realloc(void *block, size_t size, struct mulle_allocator *alloc);
void  my_mmap_free(void *block, struct mulle_allocator *alloc);

```

### Step 2: Instantiate the Allocator Structure

Initialize a global or local `struct mulle_allocator` with your custom functions. You can store context data in the optional `aba` field (typically `NULL` for simple cases).

```c
static struct mulle_allocator my_mmap_allocator = {
    my_mmap_calloc,
    my_mmap_realloc,
    my_mmap_free,
    mulle_allocation_fail,          /* keep default abort-on-fail behaviour */
    mulle_allocator_no_aba_abort,   /* default ABA abort */
    NULL                            /* optional per-allocator context data */
};

```

### Step 3: Use the Custom Allocator

All public APIs accept a `struct mulle_allocator *` argument, or fall back to the global default when `NULL` is passed. Pass your custom instance wherever you need mapped memory.

```c
/* allocate 64 KiB inside a shared-memory segment */
void *buf = mulle_allocator_malloc(&my_mmap_allocator, 64 * 1024);

/* later … */
mulle_allocator_free(&my_mmap_allocator, buf);

```

## Complete Working Example: mmap-Backed Allocator

The following example demonstrates a minimal `mmap`-based allocator that allocates page-aligned anonymous memory. It handles `malloc`, `realloc`, and `free` through `mmap` and `munmap` system calls.

```c
#include "mulle-allocator.h"
#include <sys/mman.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

/* -------------------------------------------------------------
   Simple mmap-backed allocator (page-aligned, one-page chunks)
   ------------------------------------------------------------- */
static size_t page_size(void)
{
    static size_t sz;
    if (!sz) sz = sysconf(_SC_PAGESIZE);
    return sz;
}

static void *mmap_calloc(size_t n, size_t size,
                         struct mulle_allocator *alloc)
{
    size_t tot = n * size;
    size_t pg  = (tot + page_size() - 1) & ~(page_size() - 1);

    void *p = mmap(NULL, pg,
                   PROT_READ | PROT_WRITE,
                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (p == MAP_FAILED) return NULL;
    memset(p, 0, tot);
    return p;
}

static void *mmap_realloc(void *block, size_t size,
                          struct mulle_allocator *alloc)
{
    if (!block) return mmap_calloc(1, size, alloc);
    if (size == 0) { mmap_free(block, alloc); return NULL; }

    /* Very naive: always allocate a fresh mapping and copy */
    void *newb = mmap_calloc(1, size, alloc);
    if (!newb) return NULL;
    memcpy(newb, block, size); /* may over-copy a bit – demo only */
    mmap_free(block, alloc);
    return newb;
}

static void mmap_free(void *block,
                      struct mulle_allocator *alloc)
{
    /* Assume the block was allocated with a whole-page mapping */
    size_t pg = page_size();
    munmap(block, pg);
}

/* -------------------------------------------------------------
   Declare the custom allocator
   ------------------------------------------------------------- */
static struct mulle_allocator mmap_allocator = {
    mmap_calloc,
    mmap_realloc,
    mmap_free,
    mulle_allocation_fail,          /* keep default abort-on-fail */
    mulle_allocator_no_aba_abort,   /* default ABA handling */
    NULL                           /* no extra context */
};

/* -------------------------------------------------------------
   Example usage
   ------------------------------------------------------------- */
int main(void)
{
    char *msg = mulle_allocator_malloc(&mmap_allocator, 128);
    if (!msg) return 1;

    strcpy(msg, "Hello from a mmap-backed buffer!");
    printf("%s\n", msg);

    mulle_allocator_free(&mmap_allocator, msg);
    return 0;
}

```

## Key Source Files and Implementation Details

Understanding the library’s architecture helps ensure your custom allocators integrate safely:

- **[`src/mulle-allocator-struct.h`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator-struct.h)** — Contains the `struct mulle_allocator` definition (lines 53‑60) specifying the required function pointer layout.
- **[`src/mulle-allocator.c`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator.c)** — Implements the default allocator (`mulle_allocator_default`, lines 22‑32) using standard C library functions.
- **[`src/mulle-allocator.h`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator.h)** — Declares public API functions like `mulle_allocator_malloc` and `mulle_allocator_free`. All functions accept a `struct mulle_allocator *` parameter.
- **[`src/mulle-memset.h`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-memset.h)** — Provides internal helper utilities, though custom allocators do not need to interact with these.

The library performs no hidden bookkeeping on allocated blocks. As long as your custom functions correctly pair allocations with deallocations and return valid pointers or `NULL` on failure, the allocator remains consistent.

## Summary

- **mulle_allocator** delegates all work to function pointers in `struct mulle_allocator`, making it agnostic to the underlying memory source.
- You can replace the default `calloc`, `realloc`, and `free` implementations with wrappers around `mmap`, `shm_open`, or shared memory APIs.
- Define a `struct mulle_allocator` instance with your custom functions and pass it to any `mulle_allocator_*` API function.
- Keep the default `fail` and `abafree` handlers unless your use case requires specific error handling or ABA protection.
- The library imposes no internal constraints on the pointer values, allowing seamless mixing of heap and mapped memory allocators within the same application.

## Frequently Asked Questions

### Can I mix custom allocators with the default heap allocator?

Yes. You can keep `mulle_allocator_default` for ordinary heap allocations and pass your custom `struct mulle_allocator *` only where memory-mapped or shared memory is required. This selective approach allows different subsystems to use different memory sources without global changes.

### Do I need to handle ABA protection in custom allocators?

No, unless your specific concurrency model requires it. You can set the `abafree` field to `mulle_allocator_no_aba_abort` (the default) to disable ABA-specific handling. If your shared memory implementation needs special synchronization, you would provide a custom `abafree` function.

### What function signatures must custom allocators implement?

You must provide three primary callbacks matching the definitions in [`src/mulle-allocator-struct.h`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator-struct.h):
- `void *calloc(size_t n, size_t size, struct mulle_allocator *alloc)`
- `void *realloc(void *block, size_t size, struct mulle_allocator *alloc)`
- `void free(void *block, struct mulle_allocator *alloc)`

Optionally implement `fail` and `abafree` handlers, though the library defaults are sufficient for most use cases.

### Is mulle_allocator thread-safe with mmap-backed implementations?

Thread safety depends entirely on your custom function implementations. The `mulle_allocator` structure itself contains no mutable state during allocation operations—it merely dispatches to your provided function pointers. If your `mmap`, `munmap`, or synchronization primitives are thread-safe, the allocator is thread-safe. For shared memory across processes, you must implement appropriate locking within your custom callbacks.