Allocating memory can be a somewhat hot-button issue in low-level languages such as C. Whereas most higher-level languages, compiled or interpreted, yield the task of allocating sufficient memory to the programmer, in C you have to grind your own wheat to cook bread. And most often than not, the mill is provided for you. Functions such as malloc
, calloc
, and alloca
(the latter close to the subject of this code) exist to let the user dynamically allocate whatever amont of memory he or she wants. But once allocated, one must bear the responsibility for that block. And after you are done with that block, you must call the free function, otherwise those pages will remain mapped. Also, it depends on your implementation of malloc as to how fragmented the memory will become upon successive calls to malloc. In their survey of memory allocation methods, Wilson el al state that the job of a memory allocator is to keep fragmentation from happening, otherwise, programmers themselves could make calls to sbrk
or brk
, or when provided, mmap
, in order to increase the heap size of the process. The Windows API includes functions such as HeapAllocate
which make the task much easier. Also in their survey is the various methods provided for dynamic memory allocation.
But dynamic memory allocation is not at all the end-all-be-all to memory allocation. With careful utilization of what is called Arena Memory Allocation, we can achieve better results, especially if we do static memory allocation, as in, allocate on the stack rather than the heap. This makes our task much easier, and we won’t have to worry about safety at all. You can initiate the region at the beginning of your program, make your function calls and pass the static region to it. At the end it comes down to how much memory your program needs. For a game, it could be harmful and may cause the function to run out of memory. For a simple program, it is entirely possible to run several contexts of a static arena.
In memorygrants.h
I have implemented a static arena allocator that does this job neat and fast. It is based on this paper. Here's an example of its usage:
#include <string.h>
#include "memorygrants.h"
_grantmem_inline uintptr_t
gcopy_callback(void *src, void *dst, size_t size) {
return (uintptr_t)memmove(dst, src, (unsigned long)size);
}
int
main() {
GranterT granter;
int a = 124;
granter_init(&granter);
grantid_t grantid = granter_grant(&granter, 4, GRANT_READ | GRANT_WRITE);
void *ret = (void*)granter_action(&granter, grantid, &a, gcopy_callback, ACTION_WRITE);
}
Compile with -D INLINE_LEVEL=1
so inlining can be forced.
So every grantid_t
is meant to be used separately as an arena. The flags provide protection. This protection is not needed in a way that, say, mmap
, provides memory protection through the MMU, rather, it is mainly symbolic.
As mentioned above, every GranterT
context needs to be executed within the same stack frame, otherwise, the functions would not be reentrant anymore and other than that, the stack into which the arena is allocated is only valid within the same frame.
You can think of the grants
array inside the GranterT
struct as a hashmap which keeps the state of granted objects. The reason the size of this array is less than the size of the region is that, one rarely needs to be granted regions of size 1 in bytes.
If you have any questions, I will be happy to answer but it is rather straightforward: ask for grants, get the grant, use the grant's id to act upon that region. This action is defined inside the gcallback_t
callback function, which has the same signature as memmove
: uintptr_t (*gcallback_t)(void *, void *, size_t)
.
Enjoy.