Skip to content

Instantly share code, notes, and snippets.

@Chubek
Last active July 22, 2023 06:49
Show Gist options
  • Save Chubek/6fa5cb170dd513a56e61460306b6d02f to your computer and use it in GitHub Desktop.
Save Chubek/6fa5cb170dd513a56e61460306b6d02f to your computer and use it in GitHub Desktop.
Static Memory Grant System in C

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.

#ifndef MEMORYGRANTS_H_
#define MEMORYGRANTS_H_
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
#ifndef REGION_SIZE
#define REGION_SIZE 16384
#endif
#ifndef GRANT_NUM_MAX
#define GRANT_NUM_MAX 1024
#endif
#if INLINE_LEVEL == 0
#define _grantmem_inline static inline
#elif INLINE_LEVEL == 1 && defined(__GNUC__)
#define _grantmem_inline static inline __attribute__((always_inline))
#elif INLINE_LEVEL == 1 && defined(_MSC_VER)
#define _grantmem_inline static inline __forceinline
#endif
#define GRANT_READ 2
#define GRANT_WRITE 4
#define GRANT_EXECUTE 8
#define GRANT_HAS(GRANT, HAS) ((GRANT & HAS) == HAS)
#define ACTION_READ 1
#define ACTION_WRITE 2
#define ACTION_EXECUTE 4
#define FLAGS_ASSEMBLE(GRANT) \
(GRANT.readable) | (GRANT.writable << 1) | (GRANT.executable << 2)
typedef uintptr_t (*gcallback_t)(void *, void *, size_t);
typedef uint32_t grantid_t;
typedef struct {
void *address;
size_t size;
uint8_t readable : 1;
uint8_t writable : 1;
uint8_t executable : 1;
} GrantT, GrantP;
typedef struct {
uintptr_t region[REGION_SIZE];
GrantP grants[GRANT_NUM_MAX];
uintptr_t *top;
} GranterT, *GranterP;
_grantmem_inline void granter_init(GranterP granter) {
granter->top = &granter->region[0];
memset(granter->top, 0, sizeof(uintptr_t) * REGION_SIZE);
}
_grantmem_inline grantid_t granter_grant(GranterP granter, size_t size,
int gflags) {
grantid_t granter_id = __COUNTER__ % GRANT_NUM_MAX;
granter->grants[granter_id].address = granter->top;
granter->grants[granter_id].size = size;
granter->grants[granter_id].readable = GRANT_HAS(gflags, GRANT_READ);
granter->grants[granter_id].writable = GRANT_HAS(gflags, GRANT_WRITE);
granter->grants[granter_id].executable = GRANT_HAS(gflags, GRANT_EXECUTE);
granter->top += size;
return granter_id;
}
_grantmem_inline uintptr_t granter_action(GranterP granter, grantid_t id,
void *addr, gcallback_t callback,
int aflags) {
GrantT grant = granter->grants[id];
int gflags = FLAGS_ASSEMBLE(grant);
if (!(gflags & aflags))
return EACCES;
return callback(addr, grant.address, grant.size);
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment