Using mulle_allocator with Memory-Mapped Files or Shared Memory

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 (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 (lines 27‑31), which simply dereferences these pointers.

The default implementation, mulle_allocator_default defined in 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.

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).

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.

/* 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.

#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 — Contains the struct mulle_allocator definition (lines 53‑60) specifying the required function pointer layout.
  • src/mulle-allocator.c — Implements the default allocator (mulle_allocator_default, lines 22‑32) using standard C library functions.
  • 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 — 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:

  • 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →