mirror of
https://codeberg.org/andyscott/marCsweep.git
synced 2024-12-22 09:43:12 -05:00
Compare commits
4 commits
37d5cfd532
...
87dfec1942
Author | SHA1 | Date | |
---|---|---|---|
87dfec1942 | |||
e8f5fa88b5 | |||
3ecb5d560f | |||
5749c68035 |
2 changed files with 226 additions and 17 deletions
|
@ -2,11 +2,13 @@
|
||||||
#define GC_MALLOC_H
|
#define GC_MALLOC_H
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
// Generally, inline functions are preferable to macros resembling functions.
|
// Generally, inline functions are preferable to macros resembling functions.
|
||||||
// https://www.kernel.org/doc/html/v4.10/process/coding-style.html#data-structures
|
// 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 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 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 */
|
#define MIN 3 /* Minimum number of 8-byte segments for data in a chunk */
|
||||||
|
@ -14,38 +16,54 @@
|
||||||
/*
|
/*
|
||||||
* Metadata and user data for allocated memory
|
* 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
|
* size: size of chunk in bytes
|
||||||
* merge: flag to identify when the chunk can be coalesced
|
* merge: flag to identify when the chunk can be coalesced,
|
||||||
* free: flag to identify when the chunk is free
|
* 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
|
* next: pointer to next chunk in the free list
|
||||||
* prev: pointer to previous chunk in the free list
|
* prev: pointer to previous chunk in the free list
|
||||||
*/
|
*/
|
||||||
struct gc_chunk {
|
struct gc_chunk {
|
||||||
uint64_t footer;
|
uint64_t footer;
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
uint16_t merge;
|
uint16_t merge; /* 1 = do merge, initialize to 0 */
|
||||||
uint16_t free;
|
uint16_t free; /* 0 = allocated, initialize to 1 */
|
||||||
struct gc_chunk *next;
|
struct gc_chunk *next;
|
||||||
struct gc_chunk *prev;
|
struct gc_chunk *prev;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Gets a pointer to the next chunk
|
// 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
|
// 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);
|
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);
|
uint64_t 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 (first fit) 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
|
// Performs allocations of SIZE bytes
|
||||||
void *gc_malloc(size_t size);
|
void *gc_malloc(size_t size);
|
||||||
|
|
||||||
// Frees blocks of memory allocated by 'gc_alloc'
|
// Frees allocations for re-use
|
||||||
void gc_free(void *ptr);
|
void gc_free(void *ptr);
|
||||||
|
|
||||||
|
// Completely de-allocates the heap (break glass in case of emergency)
|
||||||
|
void destroy_heap(struct gc_chunk *freelist);
|
||||||
|
|
||||||
#endif /* GC_MALLOC_H */
|
#endif /* GC_MALLOC_H */
|
||||||
|
|
203
src/gc_malloc.c
203
src/gc_malloc.c
|
@ -1,9 +1,18 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "gc_malloc.h"
|
#include "gc_malloc.h"
|
||||||
|
|
||||||
|
/* Static Variables */
|
||||||
|
|
||||||
|
// Doubly linked list to keep track of free chunks
|
||||||
|
static struct gc_chunk *freelist = NULL;
|
||||||
|
|
||||||
|
/* Static Inline Functions */
|
||||||
|
|
||||||
// Hides chunk header
|
// Hides chunk header
|
||||||
static inline void *hide(struct gc_chunk *curr)
|
static inline void *hide(struct gc_chunk *curr)
|
||||||
{
|
{
|
||||||
|
@ -16,27 +25,209 @@ static inline struct gc_chunk *magic(void *mem)
|
||||||
return (struct gc_chunk *)((uint64_t *)mem - MAGIC);
|
return (struct gc_chunk *)((uint64_t *)mem - MAGIC);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aligns the amount of memory requested by the user with chunk size
|
/*
|
||||||
static inline size_t align(size_t size)
|
* Aligns the amount of memory requested by the user with chunk 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 + (ALIGN - 1)) & ~(ALIGN - 1);
|
||||||
size = (size + (alignment - 1)) & ~(alignment - 1);
|
|
||||||
|
|
||||||
|
// REVIEW: unnecessary with bit-wise math above?
|
||||||
if (size <= MIN)
|
if (size <= MIN)
|
||||||
return MIN;
|
return MIN;
|
||||||
else
|
else
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct gc_chunk *get_next_chunk(struct gc_chunk *curr)
|
/* Debug Functions */
|
||||||
|
|
||||||
|
// Useful for debugging after find or merge are called
|
||||||
|
static void chk_freelist(void)
|
||||||
|
{
|
||||||
|
struct gc_chunk *next = freelist;
|
||||||
|
struct gc_chunk *prev = NULL;
|
||||||
|
|
||||||
|
while (next != NULL) {
|
||||||
|
assert(next->free);
|
||||||
|
assert(next->merge == 0);
|
||||||
|
assert(next->size >= MIN);
|
||||||
|
assert(get_footer(next) == next->size);
|
||||||
|
assert(next->prev == prev);
|
||||||
|
struct gc_chunk *after = find_next_chunk(next);
|
||||||
|
assert(after->merge == 1);
|
||||||
|
assert(after->free == 0);
|
||||||
|
prev = next;
|
||||||
|
next = next->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* gc_malloc Functions */
|
||||||
|
|
||||||
|
struct gc_chunk *find_next_chunk(struct gc_chunk *curr)
|
||||||
{
|
{
|
||||||
unsigned size = curr->size;
|
unsigned size = curr->size;
|
||||||
return (struct gc_chunk *)((uint64_t *)curr + MAGIC + (size - 1));
|
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);
|
assert(curr->merge);
|
||||||
unsigned size = curr->footer;
|
unsigned size = curr->footer;
|
||||||
return (struct gc_chunk *)((uint64_t *)curr - (size - 1) - MAGIC);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t 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 len = getpagesize();
|
||||||
|
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 = len - 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;
|
||||||
|
|
||||||
|
chk_freelist(); // TODO: Remove this line after debugging
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct gc_chunk *find_free_chunk(size_t size)
|
||||||
|
{
|
||||||
|
// HACK: The first loop will run forever if we can never get a big enough chunk
|
||||||
|
const size_t page = getpagesize();
|
||||||
|
if (size > page) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"ERROR: find_free_chunk: Requested size is larger than a page!");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct gc_chunk *found;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
found = freelist;
|
||||||
|
while (found != NULL) {
|
||||||
|
if (found->size >= size) {
|
||||||
|
// found, TODO: split when appropriate
|
||||||
|
// remaining block needs to be at least MIN + OVERHEAD
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
found = found->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found == NULL) {
|
||||||
|
found = init_gc_chunk();
|
||||||
|
if (found == NULL) {
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
freelist = found;
|
||||||
|
break;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: need to clean up todos before this will work
|
||||||
|
void merge_chunk(struct gc_chunk *curr)
|
||||||
|
{
|
||||||
|
if (curr->merge) {
|
||||||
|
struct gc_chunk *prev = find_prev_chunk(curr);
|
||||||
|
chk_freelist(); // TODO: Remove this line after debugging
|
||||||
|
struct gc_chunk *next = find_next_chunk(curr);
|
||||||
|
chk_freelist(); // TODO: Remove this line after debugging
|
||||||
|
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);
|
||||||
|
chk_freelist(); // TODO: Remove this line after debugging
|
||||||
|
if (next->free) {
|
||||||
|
// TODO Case 3: merge with after
|
||||||
|
} else {
|
||||||
|
// TODO Case 4: can't merge anything
|
||||||
|
}
|
||||||
|
curr->free = 1;
|
||||||
|
set_footer(curr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void *gc_malloc(size_t size)
|
||||||
|
{
|
||||||
|
if (size == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
uint32_t bytes = align(size);
|
||||||
|
struct gc_chunk *curr = find_free_chunk(bytes);
|
||||||
|
chk_freelist(); // TODO: Remove this line after debugging
|
||||||
|
|
||||||
|
if (curr == NULL)
|
||||||
|
return curr;
|
||||||
|
|
||||||
|
return hide(curr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gc_free(void *ptr)
|
||||||
|
{
|
||||||
|
if (ptr != NULL) {
|
||||||
|
struct gc_chunk *curr = magic(ptr);
|
||||||
|
merge_chunk(curr);
|
||||||
|
chk_freelist(); // TODO: Remove this line after debugging
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_heap(struct gc_chunk *freelist)
|
||||||
|
{
|
||||||
|
if (freelist == NULL) {
|
||||||
|
fprintf(stderr, "WARN: destroy_heap: Free list is empty?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct gc_chunk *curr = freelist;
|
||||||
|
while (curr->size != 0) {
|
||||||
|
struct gc_chunk *next = curr->next;
|
||||||
|
munmap(curr, curr->size);
|
||||||
|
curr = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue