Ioto Memory Allocator
Ioto provides a wrapper over the standard malloc memory allocator. This wrapper performs global memory allocation checking and is tailored to the needs of embedded applications.
Memory Allocation
It is difficult and error-prone for programmers to check the result of every API call that can possibly fail due to memory allocation errors. Calls such as strdup and asprintf are often assumed to succeed, but they can, and do fail when memory is depleted.
A better approach is to proactively detect and handle memory allocation errors in one place. The Ioto allocator handles memory allocation errors globally. When Ioto detects a memory allocation failure, it invokes the global memory exception handler. This configurable handler can then decide what is the best course of recovery. The default action is to abort Ioto so that it can be cleanly restarted.
Wrapper Routines
The safe runtime provides three memory allocation routines that wrap the standard libc routines.
- rAlloc — Allocate memory
- rFree — Free memory
- rAllocType — Allocate memory for a given type
The rAlloc routine allocates memory and checks for memory failures and invokes the global exception handler if the memory allocation fails.
The rFree routine frees memory and is NULL tolerant. This routine accepts memory allocated via rAlloc or malloc.
The rAllocType routine is a convenience function to allocate memory for a typed object. For example:
mem = rAllocType(struct shape);Memory Error Handler
The default memory handler prints a message regarding the memory allocation error and aborts execution.
In many cases, the best recovery is to log the error and quickly reboot the service to minimize down-time. If a slow memory leak is the culprit in your code, then this approach may be sufficient, though clearly not ideal!
You can replace the default handler to perform custom error recovery. Use the rSetMemHandler API to install your own memory handler.
void myHandler(int cause, size_t size)
{
fprintf(stderr, "Memory allocation error for %zd bytes", size);
// Try to recover
}
rSetMemoryHandler(myHandler);Virtual Memory APIs
The Safe Runtime provides virtual memory allocation APIs that are used primarily for fiber stack management. Unlike standard heap allocation, virtual memory allocation uses operating system primitives (mmap on Unix-like systems, VirtualAlloc on Windows) to allocate memory directly from the OS. This approach keeps fiber stack allocations separate from the general heap, reducing memory fragmentation and enabling advanced features like guard pages and growable stacks.
Fiber stacks benefit from virtual memory allocation because each fiber requires a dedicated stack region. By using virtual memory, the runtime can reserve large address spaces for potential stack growth while only committing physical memory as needed. Guard pages at stack boundaries provide hardware-enforced stack overflow detection, catching runaway recursion before it corrupts adjacent memory.
rAllocVirt
Allocate memory via virtual memory allocation.
void *rAllocVirt(size_t size);| Parameter | Description |
|---|---|
| size | Size of memory region to allocate in bytes |
Returns: Pointer to allocated memory, or NULL on failure.
This function allocates committed, read-write memory using OS virtual memory primitives. On Unix-like systems it uses mmap with MAP_ANONYMOUS, on Windows it uses VirtualAlloc with MEM_COMMIT | MEM_RESERVE. On platforms without virtual memory support (e.g., ESP32), it falls back to standard heap allocation.
rFreeVirt
Free memory allocated via rAllocVirt.
void rFreeVirt(void *ptr, size_t size);| Parameter | Description |
|---|---|
| ptr | Pointer to memory region to free |
| size | Size of the memory region |
This function releases memory previously allocated with rAllocVirt. The size parameter must match the original allocation size. The function is NULL-tolerant.
rAllocPages
Reserve virtual address space without committing physical memory.
void *rAllocPages(size_t size);| Parameter | Description |
|---|---|
| size | Size of address space to reserve in bytes |
Returns: Pointer to reserved address space, or NULL on failure.
This function reserves virtual address space with PROT_NONE protection (no read, write, or execute access). The reserved pages consume virtual address space but not physical memory until committed via rProtectPages. This is used for growable fiber stacks where a large address range is reserved but memory is committed incrementally as the stack grows.
Note: This function is only available when ME_FIBER_GROWABLE_STACK is enabled.
rFreePages
Free reserved virtual address space.
void rFreePages(void *ptr, size_t size);| Parameter | Description |
|---|---|
| ptr | Pointer to reserved address space |
| size | Size of the reserved region |
This function releases address space previously reserved with rAllocPages. The function is NULL-tolerant.
Note: This function is only available when ME_FIBER_GROWABLE_STACK is enabled.
rProtectPages
Change memory protection on a virtual memory region.
int rProtectPages(void *addr, size_t size, int prot);| Parameter | Description |
|---|---|
| addr | Address of memory region (must be page-aligned) |
| size | Size of the region to protect |
| prot | Protection flags: R_PROT_READ, R_PROT_WRITE, R_PROT_EXEC |
Returns: 0 on success, -1 on failure.
This function modifies the protection attributes of a memory region. It can be used to commit previously reserved pages by changing protection from PROT_NONE to read/write. Protection flags can be combined with bitwise OR.
| Flag | Description |
|---|---|
R_PROT_READ | Allow read access |
R_PROT_WRITE | Allow write access |
R_PROT_EXEC | Allow execute access |
Note: This function is only available when ME_FIBER_GROWABLE_STACK is enabled.
rGetPageSize
Get the system memory page size.
size_t rGetPageSize(void);Returns: System page size in bytes.
This function returns the operating system's memory page size, which is typically 4096 bytes on most systems. The value is cached after the first call for efficiency. This is useful for aligning memory allocations to page boundaries when working with virtual memory.
Note: This function is only available when ME_FIBER_GROWABLE_STACK is enabled.
