From 5749c68035540bb2aa1e18d40562a0045f38f671 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Wed, 18 Sep 2024 13:54:53 -0400 Subject: [PATCH] malloc: defined `set_footer`, `get_footer`, `init_gc_chunk` `find_free_chunk` and `merge_chunk` --- include/gc_malloc.h | 33 +++++++++---- src/gc_malloc.c | 118 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 137 insertions(+), 14 deletions(-) diff --git a/include/gc_malloc.h b/include/gc_malloc.h index a3e780f..b3c97b0 100644 --- a/include/gc_malloc.h +++ b/include/gc_malloc.h @@ -2,11 +2,13 @@ #define GC_MALLOC_H #include +#include #include // Generally, inline functions are preferable to macros resembling functions. // https://www.kernel.org/doc/html/v4.10/process/coding-style.html#data-structures +#define ALIGN 8 /* Number of bytes to which chunk size is aligned */ #define OVERHEAD 1 /* Number of 8-byte segments for chunk size & flags */ #define MAGIC 2 /* Number of 8-byte segments from top of chunk to fields */ #define MIN 3 /* Minimum number of 8-byte segments for data in a chunk */ @@ -14,34 +16,47 @@ /* * Metadata and user data for allocated memory * - * footer: user data for previous chunk + * footer: size of previous block or user data of previous block if allocated * size: size of chunk in bytes - * merge: flag to identify when the chunk can be coalesced - * free: flag to identify when the chunk is free + * merge: flag to identify when the chunk can be coalesced, + * possible values 0 (false, don't merge) or non-zero (true, do merge) + * free: flag to identify when the chunk is free, + * possible values 0 (false, allocated) or non-zero (true, chunk is free) * next: pointer to next chunk in the free list * prev: pointer to previous chunk in the free list */ struct gc_chunk { uint64_t footer; uint32_t size; - uint16_t merge; - uint16_t free; + uint16_t merge; /* 1 = do merge, initialize to 0 */ + uint16_t free; /* 0 = allocated, initialize to 1 */ struct gc_chunk *next; struct gc_chunk *prev; }; // Gets a pointer to the next chunk -struct gc_chunk *get_next_chunk(struct gc_chunk *curr); +struct gc_chunk *find_next_chunk(struct gc_chunk *curr); // Gets a pointer to the previous chunk, ensuring that current can be coalesced -struct gc_chunk *get_prev_chunk(struct gc_chunk *curr); +struct gc_chunk *find_prev_chunk(struct gc_chunk *curr); -// Updates chunk footer information +// Updates chunk footer information (remember that the current chunk's footer +// data is actually part of the next chunk) void set_footer(struct gc_chunk *curr); -// Gets footer information for the current chunk +// Gets footer information for the current chunk - useful for debugging int get_footer(struct gc_chunk *curr); +// Creates a new chunk with sentinel +struct gc_chunk *init_gc_chunk(void); + +// Gets a pointer to a suitable chunk from the free list, otherwise creates a +// new chunk and returns it's pointer +struct gc_chunk *find_free_chunk(size_t size); + +// Coalesces chunks when merge flag is set +void merge_chunk(struct gc_chunk *curr); + // Performs allocations of SIZE bytes void *gc_malloc(size_t size); diff --git a/src/gc_malloc.c b/src/gc_malloc.c index 547e9d3..544a98a 100644 --- a/src/gc_malloc.c +++ b/src/gc_malloc.c @@ -1,9 +1,12 @@ #include #include #include +#include #include "gc_malloc.h" +static struct gc_chunk *freelist = NULL; + // Hides chunk header static inline void *hide(struct gc_chunk *curr) { @@ -17,26 +20,131 @@ static inline struct gc_chunk *magic(void *mem) } // Aligns the amount of memory requested by the user with chunk size -static inline size_t align(size_t size) +/* + * Example: User requests 1 byte + * LHS + * requested size = 0b00000001 + * alignment - 1 = 0b00000111 + * => size + (alignment - 1) = 0b00001000 + * RHS + * ~(alignment - 1) = 0b11111000 + * LHS & RHS = 0b00001000 + */ +// TODO: should probably be 16? would need to change chunk struct +static inline uint32_t align(uint32_t size) { - const size_t alignment = 8; - size = (size + (alignment - 1)) & ~(alignment - 1); + size = (size + (ALIGN - 1)) & ~(ALIGN - 1); + // REVIEW: unnecessary with bit-wise math above? if (size <= MIN) return MIN; else return size; } -struct gc_chunk *get_next_chunk(struct gc_chunk *curr) +struct gc_chunk *find_next_chunk(struct gc_chunk *curr) { unsigned size = curr->size; return (struct gc_chunk *)((uint64_t *)curr + MAGIC + (size - 1)); } -struct gc_chunk *get_prev_chunk(struct gc_chunk *curr) +struct gc_chunk *find_prev_chunk(struct gc_chunk *curr) { assert(curr->merge); unsigned size = curr->footer; return (struct gc_chunk *)((uint64_t *)curr - (size - 1) - MAGIC); } + +void set_footer(struct gc_chunk *curr) +{ + struct gc_chunk *next = find_next_chunk(curr); + next->footer = curr->size; +} + +int get_footer(struct gc_chunk *curr) +{ + struct gc_chunk *next = find_next_chunk(curr); + return next->footer; +} + +struct gc_chunk *init_gc_chunk(void) +{ + const size_t page_size = getpagesize(); + const size_t len = page_size * sizeof(uint64_t); + struct gc_chunk *chunk = + (struct gc_chunk *)mmap(NULL, len, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + + if (chunk == MAP_FAILED) + return NULL; + + chunk->size = page_size - MAGIC - OVERHEAD; + chunk->merge = 0; + chunk->free = 1; + chunk->next = NULL; + chunk->prev = NULL; + + struct gc_chunk *sentinel = find_next_chunk(chunk); + sentinel->footer = chunk->size; + sentinel->size = 0; + sentinel-> merge = 1; + sentinel->free = 1; + sentinel->next = NULL; + sentinel->prev = chunk; + + return chunk; +} + +struct gc_chunk *find_free_chunk(size_t size) +{ + struct gc_chunk *found; + + while (1) { + found = freelist; + while (found != NULL) { + if (found->size >= size) { + // found, TODO: split when appropriate + break; + } + found = found->next; + } + + if (found == NULL) { + found = init_gc_chunk(); + if (found == NULL) { + return found; + } + freelist = found; + break; + + } else { + break; + } + } + + return found; +} + +void merge_chunk(struct gc_chunk *curr) +{ + if (curr->merge) { + struct gc_chunk *prev = find_prev_chunk(curr); + struct gc_chunk *next = find_next_chunk(curr); + if (next->free) { + // TODO Case 1: merge prev and next + + } else { + // TODO Case 2: merge with prev + } + set_footer(prev); + } else { + struct gc_chunk *next = find_next_chunk(curr); + if (next->free) { + // TODO Case 3: merge with after + } else { + // TODO Case 4: can't merge anything + } + curr->free = 1; + set_footer(curr); + } +}