Skip to content

Commit

Permalink
Initial Collector + Allocator fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
ysbaddaden committed Dec 13, 2017
1 parent e4c23f0 commit c4a7713
Show file tree
Hide file tree
Showing 17 changed files with 750 additions and 339 deletions.
11 changes: 7 additions & 4 deletions c/Makefile
@@ -1,15 +1,16 @@
.POSIX:

CFLAGS = -g -fPIC -O3 -Wall -pedantic -std=c99 -D_BSD_SOURCE $(CUSTOM)
CFLAGS = $(CUSTOM) -g -fPIC -O3 -Wall -pedantic -std=c99 -D_BSD_SOURCE
LDFLAGS = $(PWD)/immix.a -lm

OBJECTS = build/immix.o \
build/global_allocator.o
build/global_allocator.o \
build/collector.o

all: immix.a

immix.a: $(OBJECTS)
$(AR) -rc immix.a build/immix.o build/global_allocator.o
$(AR) -rc immix.a build/immix.o build/global_allocator.o build/collector.o

build/%.o: %.c *.h
@mkdir -p build
Expand All @@ -23,8 +24,10 @@ setup: phony
[ -f test/greenest ] && wget https://raw.githubusercontent.com/silentbicycle/greatest/v1.3.1/contrib/greenest -O test/greenest
chmod +x test/greenest

test: phony immix.a
build/test-runner: immix.a test/*.c test/*.h
$(CC) -rdynamic $(CFLAGS) -o build/test-runner test/runner.c $(LDFLAGS)

test: phony build/test-runner
./build/test-runner | ./test/greenest

phony:
150 changes: 123 additions & 27 deletions c/chunk_list.h
Expand Up @@ -5,6 +5,7 @@
#include <stdint.h>
#include <assert.h>
#include "object.h"
#include "utils.h"

typedef struct Chunk {
struct Chunk *next;
Expand All @@ -13,64 +14,159 @@ typedef struct Chunk {
} Chunk;

#define CHUNK_HEADER_SIZE (sizeof(Chunk) - sizeof(Object))
#define CHUNK_MIN_SIZE (sizeof(Chunk) * 2)

inline void Chunk_init(Chunk *chunk, size_t size) {
static inline void Chunk_init(Chunk *chunk, size_t size) {
chunk->next = NULL;
chunk->allocated = 0;
chunk->object.size = size;

// not required:
chunk->object.marked = 0;
chunk->object.atomic = 0;
}

static inline void Chunk_unmark(Chunk *chunk) {
chunk->object.marked = 0;
}

static inline int Chunk_isMarked(Chunk *chunk) {
return chunk->object.marked == 1;
}

inline void* Chunk_mutatorAddress(Chunk *chunk) {
static inline void* Chunk_mutatorAddress(Chunk *chunk) {
return (char*)chunk + sizeof(Chunk);
}

static inline int Chunk_contains(Chunk *chunk, void *pointer) {
void *start = Chunk_mutatorAddress(chunk);
void *stop = (char *)chunk + CHUNK_HEADER_SIZE + chunk->object.size;
return (pointer >= start) && (pointer < stop);
}


typedef struct {
Chunk *first;
Chunk *last;
size_t size;
} ChunkList;

inline void ChunkList_clear(ChunkList *chunk_list) {
chunk_list->first = NULL;
chunk_list->last = NULL;
chunk_list->size = 0;
static inline void ChunkList_clear(ChunkList *self) {
self->first = NULL;
self->last = NULL;
self->size = 0;
}

inline int ChunkList_isEmpty(ChunkList *chunk_list) {
return chunk_list->first == NULL;
static inline int ChunkList_isEmpty(ChunkList *self) {
return self->first == NULL;
}

inline void ChunkList_push(ChunkList *chunk_list, Chunk *chunk) {
if (chunk_list->first == NULL) {
chunk_list->first = chunk;
static inline void ChunkList_push(ChunkList *self, Chunk *chunk) {
chunk->next = NULL;

if (ChunkList_isEmpty(self)) {
self->first = chunk;
} else {
chunk_list->last->next = chunk;
self->last->next = chunk;
}
self->last = chunk;
self->size++;
}

//static inline Chunk *ChunkList_shift(ChunkList *self) {
// Chunk *chunk = self->first;
//
// if (chunk != NULL) {
// self->first = chunk->next;
// self->size--;
// }
//
// return chunk;
//}

static inline void ChunkList_insert(ChunkList *self, Chunk *chunk, Chunk* after) {
Chunk *previous = after;

if (after == self->last) {
chunk->next = NULL;
self->last->next = chunk;
self->last = chunk;
} else {
chunk->next = previous->next;
previous->next = chunk;
}
chunk->next = NULL;

chunk_list->last = chunk;
chunk_list->size++;
self->size++;
}

inline Chunk *ChunkList_shift(ChunkList *chunk_list) {
Chunk *chunk = chunk_list->first;
static inline Chunk *ChunkList_split(ChunkList *self, Chunk *chunk, size_t size) {
size_t remaining = chunk->object.size - size;

if (chunk != NULL) {
chunk_list->first = chunk->next;
chunk_list->size--;
// enough space to accomodate new chunk?
if (remaining < CHUNK_MIN_SIZE) {
return NULL;
}

return chunk;
// resize current chunk
chunk->object.size = size;

// insert new chunk (free)
Chunk *free = (Chunk *)((char *)chunk + CHUNK_HEADER_SIZE + size);
Chunk_init(free, remaining - CHUNK_HEADER_SIZE);
ChunkList_insert(self, free, chunk);

DEBUG("GC: insert chunk=%p previous=%p next=%p size=%zu\n",
(void *)free, (void *)chunk, (void *)free->next, free->object.size);

return free;
}

inline void ChunkList_insert(ChunkList *chunk_list, Chunk *chunk, Chunk* after) {
chunk->next = after->next;
after->next = chunk;
static inline void ChunkList_validate(ChunkList *self, void *heap_stop) {
Chunk *chunk = self->first;

size_t count = 0;

while (chunk != NULL) {
count++;

if (chunk->next == NULL) {
void *actual = (char *)chunk + CHUNK_HEADER_SIZE + chunk->object.size;

if (actual != heap_stop) {
fprintf(stderr, "ASSERTION FAILED: heap_stop %p == chunk+header+size %p\n", heap_stop, actual);
abort();
}

if (chunk != self->last) {
fprintf(stderr, "ASSERTION FAILED: chunk %p == self->last %p\n", (void *)chunk, (void *)self->last);
abort();
}
if (count != self->size) {
fprintf(stderr, "ASSERTION FAILED: count %zu == self->size %zu\n", count, self->size);
abort();
}
return;
}

char *expected = (char *)chunk->next;
char *actual = (char *)chunk + CHUNK_HEADER_SIZE + chunk->object.size;

if (actual != expected) {
fprintf(stderr, "ASSERTION FAILED: chunk->next %p == chunk+header+size %p\n", expected, actual);
abort();
}

chunk = chunk->next;
}
}

if (after == chunk_list->last) {
chunk_list->last = chunk;
static inline void ChunkList_debug(ChunkList *self) {
Chunk *chunk = self->first;
while (chunk != NULL) {
fprintf(stderr, "CHUNK_LIST: chunk=%p allocated=%d size=%zu marked=%d atomic=%d\n",
(void *)chunk, chunk->allocated, chunk->object.size, chunk->object.marked, chunk->object.atomic);
chunk = chunk->next;
}
chunk_list->size++;
}

#endif
108 changes: 108 additions & 0 deletions c/collector.c
@@ -0,0 +1,108 @@
#include <stdlib.h>
#include <stdio.h>
#include "collector.h"
#include "utils.h"

extern char __data_start[];
extern char __bss_start[];
extern char _end[];

// TODO: support various platforms (only linux-gnu for now)
void GC_Collector_init(Collector *self, GlobalAllocator *allocator) {
self->global_allocator = allocator;
self->collect_callback = NULL;

// DATA section (initialized constants):
self->data_start = &__data_start;
self->data_end = &__bss_start;

// BSS section (uninitialized constants):
self->bss_start = &__bss_start;
self->bss_end = &_end;
}

static inline void Collector_unmarkAll(Collector *self) {
Chunk *chunk = self->global_allocator->chunk_list.first;

while (chunk != NULL) {
Chunk_unmark(chunk);
chunk = chunk->next;
}
}

static inline void Collector_markObject(Collector *self, Object *object) {
if (!object->marked) {
object->marked = 1;

DEBUG("GC: mark ptr=%p size=%zu atomic=%d\n",
Object_mutatorAddress(object), object->size, object->atomic);

if (!object->atomic) {
void *sp = Object_mutatorAddress(object);
void *bottom = (char*)object + object->size;
Collector_markRegion(self, sp, bottom, "object");
}
}
}

static inline Chunk *Collector_findChunk(Collector *self, void *pointer) {
Chunk* chunk = self->global_allocator->chunk_list.first;

while (chunk != NULL) {
if (Chunk_contains(chunk, pointer)) {
return chunk;
}
chunk = chunk->next;
}

return NULL;
}

void GC_Collector_markRegion(Collector *self, void *top, void *bottom, const char *source) {
DEBUG("GC: mark region top=%p bottom=%p source=%s\n", top, bottom, source);
assert(top <= bottom);

void *sp = top;
while (sp < bottom) {
// dereference stack pointer's value as heap pointer:
void *pointer = (void *)(*(uintptr_t *)(sp));

if (GlobalAllocator_inLargeHeap(self->global_allocator, pointer)) {
// search chunk (may be an inner pointer):
Chunk *chunk = Collector_findChunk(self, pointer);

if (chunk == NULL) {
DEBUG("GC: failed to find chunk for ptr=%p\n", pointer);
} else if (chunk->allocated) {
Collector_markObject(self, &chunk->object);
}
}

sp = (char*)sp + sizeof(void *);
}
}

static inline void Collector_sweep(Collector *self) {
Chunk* chunk = self->global_allocator->chunk_list.first;
while (chunk != NULL) {
if (!Chunk_isMarked(chunk)) {
DEBUG("GC: free chunk=%p ptr=%p size=%zu\n",
(void *)chunk, Chunk_mutatorAddress(chunk), chunk->object.size);
chunk->allocated = 0;
}
chunk = chunk->next;
}
}

void GC_Collector_collect(Collector *self) {
DEBUG("GC: collect start\n");

Collector_unmarkAll(self);

Collector_markRegion(self, self->data_start, self->data_end, ".data");
Collector_markRegion(self, self->bss_start, self->bss_end, ".bss");
Collector_callCollectCallback(self);

Collector_sweep(self);
DEBUG("GC: collect end\n");
}
36 changes: 36 additions & 0 deletions c/collector.h
@@ -0,0 +1,36 @@
#ifndef IMMIX_COLLECTOR_H
#define IMMIX_COLLECTOR_H

#include "global_allocator.h"

typedef void (*collect_callback_t)(void);

typedef struct GC_Collector {
GlobalAllocator *global_allocator;
collect_callback_t collect_callback;
void *data_start;
void *data_end;
void *bss_start;
void *bss_end;
} Collector;

void GC_Collector_init(Collector *self, GlobalAllocator *allocator);
void GC_Collector_collect(Collector *self);
void GC_Collector_markRegion(Collector *self, void *stack_top, void *stack_bottom, const char *source);

static inline void Collector_registerCollectCallback(Collector *self, collect_callback_t callback) {
self->collect_callback = callback;
}

static inline void Collector_callCollectCallback(Collector *self) {
collect_callback_t callback = self->collect_callback;
if (callback != NULL) {
callback();
}
}

#define Collector_init GC_Collector_init
#define Collector_collect GC_Collector_collect
#define Collector_markRegion GC_Collector_markRegion

#endif
12 changes: 0 additions & 12 deletions c/gc.h

This file was deleted.

0 comments on commit c4a7713

Please sign in to comment.