|
libmemalloc
v3.0.00
Modern Memory Allocator
|
Core memory management components for libmemalloc. More...
#include <inttypes.h>#include <stdbool.h>#include <stddef.h>#include <sys/mman.h>#include <sys/resource.h>#include "libmemalloc.h"#include "logs.h"Classes | |
| struct | block_header_t |
| Represents the header for a memory block. More... | |
| struct | mem_arena_t |
| Represents a memory arena with its own free lists. More... | |
| struct | mmap_t |
| Tracks memory-mapped regions for large allocations. More... | |
| struct | gc_thread_t |
| Orchestrates the background mark-and-sweep garbage collector. More... | |
| struct | mem_allocator_t |
| Manages dynamic memory allocation. More... | |
Macros | |
| #define | LOG_LEVEL LOG_LEVEL_NONE |
| Logging verbosity threshold for this module. | |
| #define | DEFAULT_NUM_BINS (uint8_t)(10U) |
| Default number of size classes (bins) for free lists. | |
| #define | CACHE_LINE_SIZE (uint8_t)(64U) |
| Size of the CPU cache line in bytes. | |
| #define | BYTES_PER_CLASS (uint8_t)(128U) |
| Fixed byte allocation per classification unit. | |
| #define | MMAP_THRESHOLD (size_t)(128U * 1024U) |
| Threshold size for using mmap-based allocation. | |
| #define | MIN_BLOCK_SIZE (size_t)(sizeof(block_header_t) + ARCH_ALIGNMENT) |
| Defines the minimum memory block size. | |
| #define | NR_OBJS (uint16_t)(1000U) |
| Number of iterations used to scale GC sleep duration. | |
Typedefs | |
| typedef int(* | find_fn_t) (mem_allocator_t *const, const size_t, block_header_t **) |
| Type for functions that locate a suitable free block. | |
Functions | |
| void * | MEM_sbrk (const intptr_t increment) |
| Invokes sbrk-like behavior by moving the program break. | |
| static int | MEM_getSizeClass (mem_allocator_t *const allocator, const size_t size) |
| Calculates the size class index for a requested memory size. | |
| static int | MEM_allocatorInit (mem_allocator_t *const allocator) |
| Initializes the memory allocator and its internal structures. | |
| static int | MEM_validateBlock (mem_allocator_t *const allocator, block_header_t *const block) |
| Validates the integrity and boundaries of a memory block. | |
| static int | MEM_insertFreeBlock (mem_allocator_t *const allocator, block_header_t *const block) |
| Inserts a block into the appropriate free list based on its size. | |
| static int | MEM_removeFreeBlock (mem_allocator_t *const allocator, block_header_t *const block) |
| Removes a block from its free list. | |
| static int | MEM_findFirstFit (mem_allocator_t *const allocator, const size_t size, block_header_t **fit_block) |
| Searches for the first suitable free memory block in size‐class lists. | |
| static int | MEM_findNextFit (mem_allocator_t *const allocator, const size_t size, block_header_t **fit_block) |
| Searches for the next suitable free memory block using the NEXT_FIT strategy starting from the last allocated position. | |
| static int | MEM_findBestFit (mem_allocator_t *const allocator, const size_t size, block_header_t **best_fit) |
| Searches for the smallest suitable free memory block in size‐class lists (BEST_FIT). | |
| static int | MEM_splitBlock (mem_allocator_t *const allocator, block_header_t *const block, const size_t req_size) |
| Splits a memory block into allocated and free portions. | |
| static int | MEM_mergeBlocks (mem_allocator_t *const allocator, block_header_t *block) |
| Merges adjacent free memory blocks. | |
| static void * | MEM_growUserHeap (mem_allocator_t *const allocator, const intptr_t inc) |
| Expands the user heap by a specified increment. | |
| static void * | MEM_mapAlloc (mem_allocator_t *const allocator, const size_t total_size) |
| Allocates a page-aligned memory region via mmap and registers it in the allocator’s mmap list for later freeing. | |
| static int | MEM_mapFree (mem_allocator_t *const allocator, void *const addr) |
| Unmaps a previously mapped memory region and removes its metadata entry from the allocator’s mmap list. | |
| static void * | MEM_allocOp (mem_allocator_t *const allocator, const size_t size, const char *const file, const int line, const allocation_strategy_t strategy) __LIBMEMALLOC_INTERNAL_MALLOC |
| Allocates memory using the specified strategy. | |
| static void * | MEM_reallocOp (mem_allocator_t *const allocator, void *const ptr, const size_t new_size, const char *const file, const int line, const allocation_strategy_t strategy) __LIBMEMALLOC_INTERNAL_REALLOC |
| Reallocates memory with safety checks. | |
| static void * | MEM_callocOp (mem_allocator_t *const allocator, const size_t size, const char *const file, const int line, const allocation_strategy_t strategy) __LIBMEMALLOC_INTERNAL_MALLOC |
| Allocates and zero‐initializes memory. | |
| static int | MEM_freeOp (mem_allocator_t *const allocator, void *const ptr, const char *const file, const int line) |
| Releases allocated memory back to the heap. | |
| static bool | MEM_stackGrowsDown (void) |
| Determine at runtime whether the stack grows downward. | |
| static int | MEM_stackBounds (const pthread_t id, mem_allocator_t *const allocator) |
| Query and record the bounding addresses of a thread’s stack. | |
| void * | MEM_memset (void *const source, const int value, const size_t size) |
| Fills a memory block with a specified byte value using optimized operations. | |
| void * | MEM_memcpy (void *const dest, const void *src, const size_t size) |
| Copies a memory block between buffers using optimized operations. | |
| void * | MEM_allocFirstFit (const size_t size) |
| Allocates memory using the FIRST_FIT strategy. | |
| void * | MEM_allocBestFit (const size_t size) |
| Allocates memory using the BEST_FIT strategy. | |
| void * | MEM_allocNextFit (const size_t size) |
| Allocates memory using the NEXT_FIT strategy. | |
| void * | MEM_alloc (const size_t size, const allocation_strategy_t strategy) |
| Allocates memory using the specified strategy. | |
| void * | MEM_calloc (const size_t size, const allocation_strategy_t strategy) |
| Allocates and zero‐initializes memory using the specified strategy. | |
| void * | MEM_realloc (void *const ptr, const size_t new_size, const allocation_strategy_t strategy) |
| Reallocates memory with safety checks using the specified strategy. | |
| int | MEM_free (void *const ptr) |
| Releases allocated memory back to the heap. | |
Variables | |
| static mem_allocator_t | g_allocator |
| Process-wide default allocator instance. | |
| static bool | g_allocator_inited = false |
One-time initialization guard for g_allocator. | |
Core memory management components for libmemalloc.
Implements memory allocator with advanced features:
| #define BYTES_PER_CLASS (uint8_t)(128U) |
Fixed byte allocation per classification unit.
Defines the number of bytes assigned to each classification category. Ensures memory alignment and efficient block processing in data structures.
| #define CACHE_LINE_SIZE (uint8_t)(64U) |
Size of the CPU cache line in bytes.
This constant defines the cache-line size used for prefetching and alignment optimizations, ensuring memory accesses align to hardware cache boundaries for maximum performance and correctness on platforms with strict alignment requirements.
| #define DEFAULT_NUM_BINS (uint8_t)(10U) |
Default number of size classes (bins) for free lists.
Defines how many size categories (bins) the allocator will maintain for managing free memory blocks. This value affects allocation speed and fragmentation behavior.
| #define LOG_LEVEL LOG_LEVEL_NONE |
Logging verbosity threshold for this module.
Sets the minimum severity of log messages that will be compiled into this translation unit. Only log calls at or below the specified level are enabled:
| #define MIN_BLOCK_SIZE (size_t)(sizeof(block_header_t) + ARCH_ALIGNMENT) |
Defines the minimum memory block size.
Ensures each memory block is large enough to hold a block header and alignment padding.
| #define MMAP_THRESHOLD (size_t)(128U * 1024U) |
Threshold size for using mmap-based allocation.
When a requested allocation size meets or exceeds this value (128 KiB), the allocator will switch from using the heap (brk/sbrk) to using mmap for more efficient large-block handling and to reduce heap fragmentation.
| #define NR_OBJS (uint16_t)(1000U) |
Number of iterations used to scale GC sleep duration.
Specifies the multiplier applied to the base GC interval (in milliseconds) to compute the actual sleep time between successive garbage-collection cycles: sleep_time = gc_interval_ms * NR_OBJS
| find_fn_t |
Type for functions that locate a suitable free block.
| [in] | allocator | Memory allocator context in which to search. |
| [in] | size | Total size requested (including header and canary). |
| [out] | block | Address of pointer to store the found block header. |
|
static |
Initializes the memory allocator and its internal structures.
This function prepares the allocator’s heap by:
| [in] | allocator | Pointer to a mem_allocator_t instance to initialize. |
| EXIT_SUCCESS | Allocator initialized successfully. |
| -EINVAL | Invalid allocator pointer. |
| -ENOMEM | Heap alignment or arena/bin allocation failed. |
|
static |
Allocates memory using the specified strategy.
This function attempts to allocate at least size bytes of user data by choosing between heap‐based allocation (via free‐lists and optional heap growth) or mmap (for large requests > MMAP_THRESHOLD). It uses the given strategy (FIRST_FIT, NEXT_FIT, BEST_FIT) to locate a free block, grows the heap if necessary, splits a larger block to fit exactly, and records debugging metadata (source file, line). For mmap allocations it rounds up to page size and tracks the region in the allocator.
| [in] | allocator | Memory allocator context. |
| [in] | size | Number of bytes requested. |
| [in] | file | Source file name for debugging metadata. |
| [in] | line | Source line number for debugging metadata. |
| [in] | strategy | Allocation strategy. |
| ptr | Valid user pointer on success. |
| -EINVAL | allocator is NULL or size is zero. |
| -ENOMEM | Out of memory: heap grow failed, no free block found, or internal metadata allocation (e.g. mmap_t node) failed. |
| -EIO | mmap() I/O error for large allocations. |
|
static |
Allocates and zero‐initializes memory.
This function behaves like calloc(), allocating at least size bytes of zeroed memory via MEM_allocOp() and then setting all bytes to zero. It records debugging metadata (source file, line) and uses the given strategy for allocation.
| [in] | allocator | Pointer to the mem_allocator_t context. |
| [in] | size | Number of bytes to allocate. |
| [in] | file | Source file name for debugging metadata. |
| [in] | line | Source line number for debugging metadata. |
| [in] | strategy | Allocation strategy to use. |
| ptr | Valid pointer to size bytes of zeroed memory. |
| -EINVAL | allocator is NULL or size is zero. |
| -ENOMEM | Out of memory: allocation failed. |
| -EIO | I/O error for large mmap‐based allocations. |
|
static |
Searches for the smallest suitable free memory block in size‐class lists (BEST_FIT).
This function computes the starting size class for the requested size via MEM_getSizeClass(), then scans each free‐list from that class upward. It validates each candidate with MEM_validateBlock() and tracks the smallest free block that is large enough. Once a block in any class is chosen, the search stops.
| [in] | allocator | Pointer to the allocator context. |
| [in] | size | Requested allocation size in bytes. |
| [out] | best_fit | On success, set to the pointer of the free block. |
| EXIT_SUCCESS | Suitable block found successfully. |
| -EINVAL | allocator or best_fit is NULL. |
| -ENOMEM | Size calculation failed or no suitable block found. |
|
static |
Searches for the first suitable free memory block in size‐class lists.
This function computes the starting size class for the requested size via MEM_getSizeClass(), then scans each free‐list from that class upward. For each candidate block, it calls MEM_validateBlock() to ensure integrity, and returns the first block that is marked free and large enough. The found block pointer is stored in fit_block.
| [in] | allocator | Pointer to the allocator context. |
| [in] | size | Requested allocation size in bytes. |
| [out] | fit_block | On success, set to the pointer of a suitable free block. |
| EXIT_SUCCESS | Suitable block found successfully. |
| -EINVAL | allocator or fit_block are NULL; |
| -ENOMEM | Size calculation failed or no suitable block found. |
|
static |
Searches for the next suitable free memory block using the NEXT_FIT strategy starting from the last allocated position.
This function attempts to find a free block of at least size bytes by scanning the heap starting at allocator->last_allocated. If last_allocated is NULL, not free, or corrupted, it falls back to First-Fit. It wraps around to the heap start if needed, stopping once it returns to the start.
| [in] | allocator | Pointer to the allocator context. |
| [in] | size | Requested allocation size in bytes. |
| [out] | fit_block | Pointer to store the address of the found block. |
| EXIT_SUCCESS | Suitable block found and fit_block set. |
| -EINVAL | allocator or fit_block is NULL. |
| -ENOMEM | No suitable block found in heap. |
|
static |
Releases allocated memory back to the heap.
This function frees a pointer previously returned by MEM_allocOp(). It supports both heap‐based and mmap‐based allocations:
| [in] | allocator | Memory allocator context. |
| [in] | ptr | Pointer to memory to free. |
| [in] | file | Source file name for debugging metadata. |
| [in] | line | Source line number for debugging metadata. |
| EXIT_SUCCESS | Memory freed (and heap possibly shrunk) successfully. |
| -EINVAL | allocator or ptr is NULL, ptr not found in allocator, or double free detected. |
| -ENOMEM | munmap() failed when freeing an mmap region. |
| -EFAULT | Block lies outside heap and mmap regions. |
| -EPROTO | Header canary mismatch (block corrupted). |
| -EOVERFLOW | Data canary mismatch (buffer overrun detected). |
| -EFBIG | Block size extends past heap end. |
| rer<0 | Errors returned by MEM_validateBlock(), MEM_removeFreeBlock(), MEM_mergeBlocks(), or MEM_insertFreeBlock(). |
|
static |
Calculates the size class index for a requested memory size.
This function determines which size class the given allocation request belongs to by dividing the requested size by BYTES_PER_CLASS (rounding up). If the computed index exceeds the maximum available class, it will be clamped to the highest class and a warning emitted.
| [in] | allocator | Pointer to the allocator context. |
| [in] | size | Requested memory size in bytes (must be > 0). |
| ret>0 | Valid size class index. |
| -EINVAL | allocator is NULL, or size is zero. |
|
static |
Expands the user heap by a specified increment.
This function moves the program break by inc bytes via MEM_sbrk(), zeroes the newly allocated region, updates the allocator’s heap_end, and initializes a block_header_t at the start of the new region to record its size, mark it as allocated, and set its free flag to false.
| [in] | allocator | Pointer to the allocator context. |
| [in] | inc | Signed number of bytes to grow (or shrink) the heap. |
| (ret>=0) | Previous heap end address (new region start). |
| -EINVAL | allocator is NULL. |
| -ENOMEM | Heap expansion failed (out of memory). |
|
static |
Inserts a block into the appropriate free list based on its size.
This function computes the size class index for the given block by calling MEM_getSizeClass(), then pushes the block onto the head of that free list within the allocator. It updates both forward and backward links to maintain the doubly‐linked list of free blocks.
| [in] | allocator | Pointer to the allocator context. |
| [in] | block | Pointer to the block header to insert. |
| EXIT_SUCCESS | block successfully inserted. |
| -EINVAL | allocator or block is NULL. |
| -ENOMEM | Size class calculation failed (request too large). |
|
static |
Allocates a page-aligned memory region via mmap and registers it in the allocator’s mmap list for later freeing.
This function rounds up total_size to a multiple of the system page size, invokes mmap() to obtain an anonymous read/write region, then allocates an mmap_t metadata node via MEM_allocOp() and links it into allocator->mmap_list. It initializes a block_header_t and trailing canary in the mapped region to integrate with the allocator’s debugging and GC.
| [in] | allocator | Pointer to the memory allocator context. |
| [in] | total_size | Number of bytes requested. |
| ret!=MAP_FAILED | Address of the mapped region. |
| -EINVAL | allocator is NULL. |
| -EIO | mmap() failed. |
| -ENOMEM | Allocation of mmap_t metadata failed. |
|
static |
Unmaps a previously mapped memory region and removes its metadata entry from the allocator’s mmap list.
This function searches the allocator’s mmap_list for an entry matching addr, calls munmap() to unmap the region, unlinks the corresponding mmap_t metadata node, and frees it via MEM_freeOp(). Errors during munmap are returned; if metadata freeing fails, an error is logged but success is returned.
| [in] | allocator | Memory allocator context. |
| [in] | addr | Address of the memory region to unmap. |
| EXIT_SUCCESS | Region unmapped and metadata entry removed. |
| -EINVAL | allocator / addr is NULL, or not found in list. |
| -ENOMEM | munmap() failed to unmap the region. |
|
static |
Merges adjacent free memory blocks.
This function removes the specified block from its free list, then checks its immediate neighbor blocks in memory. If the next block is free and valid, it unlinks and combines it with block, updating size, links, and trailing canary. It then checks the previous block; if it is also free and valid, it merges block into the previous block. Finally, the resulting merged block is reinserted into the appropriate free list.
| [in] | allocator | Pointer to the allocator context. |
| [in] | block | Pointer to the free block header to merge. |
| EXIT_SUCCESS | Blocks merged (or single block reinserted) successfully. |
| -EINVAL | allocator or block is NULL. |
| ret<0 | Returned by MEM_removeFreeBlock(), MEM_validateBlock(), MEM_insertFreeBlock(), or munmap() in inner calls indicating the specific failure. |
|
static |
Reallocates memory with safety checks.
This function resizes an existing allocation to new_size bytes:
ptr is NULL, behaves like malloc().strategy, copies the lesser of old and new sizes, frees the old block, and returns the new pointer.| [in] | allocator | Memory allocator context. |
| [in] | ptr | Pointer to the block to resize, or NULL to allocate. |
| [in] | new_size | New requested size in bytes. |
| [in] | file | Source file name for debugging metadata. |
| [in] | line | Source line number for debugging metadata. |
| [in] | strategy | Allocation strategy to use. |
ptr) on success; an error-encoded pointer (via PTR_ERR()) on failure.| ptr | Valid pointer to a block of at least new_size bytes. |
| -EINVAL | allocator is NULL, new_size is zero. |
| -ENOMEM | Out of memory (allocation or free failure). |
| -EIO | I/O error for large mmap-based allocations. |
|
static |
Removes a block from its free list.
This function unlinks the specified block from the free list corresponding to its size class within the allocator. It computes the size‐class index via MEM_getSizeClass(), validates parameters, then adjusts the neighboring blocks’ fl_next and fl_prev pointers (or the list head) to remove block. The block’s own fl_next and fl_prev are then cleared.
| [in] | allocator | Memory allocator context. |
| [in] | block | Block header to remove. |
| EXIT_SUCCESS | Block removed successfully. |
| -EINVAL | allocator or block is NULL. |
| -ENOMEM | Size‐class calculation failed. |
| void * MEM_sbrk | ( | const intptr_t | increment | ) |
Invokes sbrk-like behavior by moving the program break.
This function reads the current program break, attempts to move it by the signed offset increment via sbrk(), and returns the original break on success. If any call to sbrk() or reading the break fails, it encodes the negative errno into a pointer via PTR_ERR.
| [in] | increment | Signed offset in bytes to move the program break: positive to grow, negative to shrink. |
| ret>-1 | Previous break on success. |
| -ENOMEM | Failed to read or adjust the break. |
|
static |
Splits a memory block into allocated and free portions.
This function takes an existing free block and a requested allocation size req_size, and divides the block into:
| [in] | allocator | Pointer to the mem_allocator_t context. |
| [in] | block | Pointer to the block_header_t to split. |
| [in] | req_size | Requested allocation size in bytes. |
| EXIT_SUCCESS | Block split (or fully allocated) successfully. |
| -EINVAL | nvalid allocator or block pointer. |
| -EPROTO | Header canary mismatch (block corrupted). |
| -EOVERFLOW | Data canary mismatch (buffer overflow detected). |
| -EFBIG | Block’s size would extend past heap end. |
| -EFAULT | Block lies outside heap or mmap regions. |
| -ENOMEM | Failed to insert the new free remainder block. |
|
static |
Query and record the bounding addresses of a thread’s stack.
This function retrieves the stack base address, total stack size, and guard size for the specified thread, then computes the usable stack bounds within the allocator object, taking into account whether the stack grows up or down in memory.
| [in] | id | The thread identifier whose stack to inspect. |
| [in] | allocator | Pointer to the allocator object where stack_bottom and stack_top will be stored. |
| EXIT_SUCCESS | Stack bounds successfully recorded. |
| -EINVAL | allocator was NULL. |
| ret>0 | Error code from one of the pthread or system calls |
|
static |
Determine at runtime whether the stack grows downward.
This function places two volatile local variables on the stack and compares their addresses to infer the growth direction:
| true | Stack grows downward (newer frames at lower addresses) |
| false | Stack grows upward (newer frames at higher addresses) |
|
static |
Validates the integrity and boundaries of a memory block.
This function ensures that the specified block lies within the allocator’s heap or one of its mmap regions, that its header canary matches the expected magic value to detect metadata corruption, that its data canary is intact to catch buffer overruns, and that the block’s size does not extend past the heap’s end. On any failure, an appropriate negative errno is returned.
| [in] | allocator | Pointer to the allocator context. |
| [in] | block | Pointer to the block header to validate. |
| EXIT_SUCCESS | block is valid. |
| -EINVAL | allocator or block pointer is NULL. |
| -EFAULT | block lies outside heap and mmap regions. |
| -EPROTO | block canary does not match expected value. |
| -EFBIG | block size causes it to extend past heap end. |
| -EOVERFLOW | block canary indicates buffer overflow. |
|
static |
Process-wide default allocator instance.
Statically allocated allocator used when the API is called without an explicit mem_allocator_t handle. This instance is initialized exactly once (see g_allocator_inited) and then reused for all subsequent allocation requests routed through the “global” path.
static grants internal linkage: the symbol is private to this translation unit.pthread_once or equivalent). Normal allocator operations should rely on the allocator’s internal locking.
|
static |
One-time initialization guard for g_allocator.
Boolean-like flag controlling lazy init of the global allocator. A value of 0 means “not initialized”; 1 means “initialized”. It should be set to 1 only after a successful call to MEM_allocatorInit(&g_allocator).
int and is not atomic. In multi-threaded contexts, protect initialization with proper synchronization (e.g., pthread_once, a mutex, or atomic CAS) to avoid races.