refined the GC. Now it calls object destructors
This commit is contained in:
@@ -1,10 +1,18 @@
|
|||||||
|
|
||||||
|
print('Start\n');
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
n = 10000000;
|
n = 1000;
|
||||||
|
|
||||||
while i < n:
|
while i < n:
|
||||||
{
|
{
|
||||||
1 + 1 * 1;
|
1 + 1 * 1;
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
|
|
||||||
|
fun printPercent()
|
||||||
print('\r', 100.0 * i / n, '% ');
|
print('\r', 100.0 * i / n, '% ');
|
||||||
|
|
||||||
|
printPercent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
print('End\n');
|
||||||
+148
-24
@@ -14,15 +14,23 @@ struct OflowAlloc {
|
|||||||
char body[];
|
char body[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Object *object;
|
||||||
|
_Bool (*destructor)(Object*, Error*);
|
||||||
|
} PendingDestruct;
|
||||||
|
|
||||||
struct xHeap {
|
struct xHeap {
|
||||||
int size;
|
int size;
|
||||||
int used;
|
int used;
|
||||||
int total;
|
int total;
|
||||||
void *body;
|
void *body;
|
||||||
OflowAlloc *oflow;
|
OflowAlloc *oflow;
|
||||||
|
PendingDestruct *pend;
|
||||||
|
int pend_size, pend_used;
|
||||||
|
|
||||||
_Bool collecting;
|
_Bool collecting;
|
||||||
_Bool collection_failed;
|
_Bool collection_failed;
|
||||||
|
int moved_with_destructors;
|
||||||
void *old_body;
|
void *old_body;
|
||||||
int old_used;
|
int old_used;
|
||||||
int old_total;
|
int old_total;
|
||||||
@@ -44,6 +52,9 @@ Heap *Heap_New(int size)
|
|||||||
heap->size = size;
|
heap->size = size;
|
||||||
heap->used = 0;
|
heap->used = 0;
|
||||||
heap->body = malloc(size);
|
heap->body = malloc(size);
|
||||||
|
heap->pend = NULL;
|
||||||
|
heap->pend_size = 0;
|
||||||
|
heap->pend_used = 0;
|
||||||
heap->oflow = 0;
|
heap->oflow = 0;
|
||||||
heap->collecting = 0;
|
heap->collecting = 0;
|
||||||
|
|
||||||
@@ -67,6 +78,21 @@ void Heap_Free(Heap *heap)
|
|||||||
VALGRIND_DESTROY_MEMPOOL(heap);
|
VALGRIND_DESTROY_MEMPOOL(heap);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Error error;
|
||||||
|
Error_Init(&error);
|
||||||
|
|
||||||
|
for(int i = 0; i < heap->pend_used; i += 1)
|
||||||
|
{
|
||||||
|
heap->pend[i].destructor(heap->pend[i].object, &error);
|
||||||
|
if(error.occurred)
|
||||||
|
{
|
||||||
|
// Errors occurred! We can't do anything about
|
||||||
|
// it now though.
|
||||||
|
Error_Free(&error);
|
||||||
|
Error_Init(&error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while(heap->oflow)
|
while(heap->oflow)
|
||||||
{
|
{
|
||||||
OflowAlloc *prev = heap->oflow->prev;
|
OflowAlloc *prev = heap->oflow->prev;
|
||||||
@@ -74,6 +100,7 @@ void Heap_Free(Heap *heap)
|
|||||||
heap->oflow = prev;
|
heap->oflow = prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(heap->pend);
|
||||||
free(heap->body);
|
free(heap->body);
|
||||||
free(heap);
|
free(heap);
|
||||||
}
|
}
|
||||||
@@ -85,6 +112,51 @@ float Heap_GetUsagePercentage(Heap *heap)
|
|||||||
|
|
||||||
void *Heap_Malloc(Heap *heap, TypeObject *type, Error *err)
|
void *Heap_Malloc(Heap *heap, TypeObject *type, Error *err)
|
||||||
{
|
{
|
||||||
|
_Bool requires_destruct = type->free != NULL;
|
||||||
|
|
||||||
|
if(requires_destruct)
|
||||||
|
{
|
||||||
|
// This type of object requires
|
||||||
|
// a destructor to be called.
|
||||||
|
if(heap->pend == NULL)
|
||||||
|
{
|
||||||
|
int n = 8;
|
||||||
|
|
||||||
|
heap->pend = malloc(n * sizeof(PendingDestruct));
|
||||||
|
|
||||||
|
if(heap->pend == NULL)
|
||||||
|
{
|
||||||
|
Error_Report(err, 1, "No memory");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
heap->pend_used = 0;
|
||||||
|
heap->pend_size = n;
|
||||||
|
}
|
||||||
|
else if(heap->pend_size == heap->pend_used)
|
||||||
|
{
|
||||||
|
int factor = 2;
|
||||||
|
|
||||||
|
void *new_pend = realloc(heap->pend, factor * heap->pend_size * sizeof(PendingDestruct));
|
||||||
|
|
||||||
|
if(new_pend == NULL)
|
||||||
|
{
|
||||||
|
Error_Report(err, 1, "No memory");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
heap->pend = new_pend;
|
||||||
|
heap->pend_size *= factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(heap->pend_size > heap->pend_used);
|
||||||
|
}
|
||||||
|
|
||||||
|
int size = type->size;
|
||||||
|
|
||||||
|
if(size < (int) sizeof(MovedObject))
|
||||||
|
size = sizeof(MovedObject);
|
||||||
|
|
||||||
void *addr = Heap_RawMalloc(heap, type->size, err);
|
void *addr = Heap_RawMalloc(heap, type->size, err);
|
||||||
|
|
||||||
if(addr == NULL)
|
if(addr == NULL)
|
||||||
@@ -101,6 +173,9 @@ void *Heap_Malloc(Heap *heap, TypeObject *type, Error *err)
|
|||||||
obj->type = type;
|
obj->type = type;
|
||||||
obj->flags = 0;
|
obj->flags = 0;
|
||||||
|
|
||||||
|
if(requires_destruct)
|
||||||
|
heap->pend[heap->pend_used++] = (PendingDestruct) { .object = obj, .destructor = obj->type->free };
|
||||||
|
|
||||||
return (Object*) addr;
|
return (Object*) addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +187,14 @@ void *Heap_RawMalloc(Heap *heap, int size, Error *err)
|
|||||||
|
|
||||||
void *addr;
|
void *addr;
|
||||||
|
|
||||||
if(heap->used + size >= heap->size)
|
int padding = heap->used;
|
||||||
|
|
||||||
|
if(heap->used & 7)
|
||||||
|
heap->used = (heap->used & ~7) + 8;
|
||||||
|
|
||||||
|
padding = heap->used - padding;
|
||||||
|
|
||||||
|
if(heap->used + size > heap->size)
|
||||||
{
|
{
|
||||||
OflowAlloc *oflow = malloc(sizeof(OflowAlloc) + size);
|
OflowAlloc *oflow = malloc(sizeof(OflowAlloc) + size);
|
||||||
|
|
||||||
@@ -123,22 +205,16 @@ void *Heap_RawMalloc(Heap *heap, int size, Error *err)
|
|||||||
heap->oflow = oflow;
|
heap->oflow = oflow;
|
||||||
|
|
||||||
addr = oflow->body;
|
addr = oflow->body;
|
||||||
|
|
||||||
heap->total += size;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int prev_used = heap->used;
|
assert(heap->used + size <= heap->size);
|
||||||
|
|
||||||
if(heap->used & 7)
|
|
||||||
heap->used = (heap->used & ~7) + 8;
|
|
||||||
|
|
||||||
addr = heap->body + heap->used;
|
addr = heap->body + heap->used;
|
||||||
|
|
||||||
heap->used += size;
|
heap->used += size;
|
||||||
heap->total += heap->used - prev_used;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
heap->total += size + padding;
|
||||||
|
|
||||||
assert(((intptr_t) addr) % 8 == 0);
|
assert(((intptr_t) addr) % 8 == 0);
|
||||||
|
|
||||||
#if USING_VALGRIND
|
#if USING_VALGRIND
|
||||||
@@ -170,6 +246,7 @@ _Bool Heap_StartCollection(Heap *heap, Error *error)
|
|||||||
heap->oflow = NULL;
|
heap->oflow = NULL;
|
||||||
heap->collecting = 1;
|
heap->collecting = 1;
|
||||||
heap->collection_failed = 0;
|
heap->collection_failed = 0;
|
||||||
|
heap->moved_with_destructors = 0;
|
||||||
heap->error = error;
|
heap->error = error;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -185,6 +262,44 @@ _Bool Heap_StopCollection(Heap *heap)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Call destructors here */
|
/* Call destructors here */
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
while(i < heap->pend_used)
|
||||||
|
{
|
||||||
|
Object *obj = heap->pend[i].object;
|
||||||
|
|
||||||
|
if(obj->flags & Object_MOVED)
|
||||||
|
{
|
||||||
|
heap->pend[i].object = ((MovedObject*) heap->pend[i].object)->new_location;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We need to call the destructor.
|
||||||
|
|
||||||
|
heap->pend[i].destructor(obj, heap->error);
|
||||||
|
|
||||||
|
if(heap->error->occurred)
|
||||||
|
return 0; // There will be leaks.
|
||||||
|
|
||||||
|
heap->pend[i] = heap->pend[heap->pend_used-1];
|
||||||
|
heap->pend_used -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(heap->pend_size / 2 > heap->pend_used)
|
||||||
|
{
|
||||||
|
// Downsize
|
||||||
|
void *temp = realloc(heap->pend, heap->pend_size / 2 * sizeof(PendingDestruct));
|
||||||
|
|
||||||
|
if(temp != NULL)
|
||||||
|
{
|
||||||
|
heap->pend = temp;
|
||||||
|
heap->pend_size /= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while(heap->old_oflow)
|
while(heap->old_oflow)
|
||||||
{
|
{
|
||||||
@@ -199,8 +314,10 @@ _Bool Heap_StopCollection(Heap *heap)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Heap_CollectExtension(void **referer, int size, Heap *heap)
|
void Heap_CollectExtension(void **referer, unsigned int size, void *userp)
|
||||||
{
|
{
|
||||||
|
Heap *heap = userp;
|
||||||
|
|
||||||
assert(referer != NULL);
|
assert(referer != NULL);
|
||||||
assert(heap->collecting);
|
assert(heap->collecting);
|
||||||
|
|
||||||
@@ -222,8 +339,10 @@ void Heap_CollectExtension(void **referer, int size, Heap *heap)
|
|||||||
*referer = new_location;
|
*referer = new_location;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Heap_CollectReference(Object **referer, Heap *heap)
|
void Heap_CollectReference(Object **referer, void *userp)
|
||||||
{
|
{
|
||||||
|
Heap *heap = userp;
|
||||||
|
|
||||||
assert(referer != NULL);
|
assert(referer != NULL);
|
||||||
assert(heap->collecting);
|
assert(heap->collecting);
|
||||||
|
|
||||||
@@ -232,24 +351,25 @@ void Heap_CollectReference(Object **referer, Heap *heap)
|
|||||||
if(heap->collection_failed || old_location == NULL)
|
if(heap->collection_failed || old_location == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if(old_location->flags & Object_MOVED)
|
||||||
|
|
||||||
|
// The object was already moved.
|
||||||
|
*referer = ((MovedObject*) old_location)->new_location;
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
Object *new_location;
|
Object *new_location;
|
||||||
|
|
||||||
|
// This object wasn't moved to
|
||||||
|
// the new heap yet.
|
||||||
|
|
||||||
if(old_location->flags & Object_STATIC)
|
if(old_location->flags & Object_STATIC)
|
||||||
|
|
||||||
// The object doesn't need to be moved
|
// The object doesn't need to be moved
|
||||||
// since it was statically allocated.
|
// since it was statically allocated.
|
||||||
new_location = old_location;
|
new_location = old_location;
|
||||||
|
|
||||||
else if(old_location->flags & Object_MOVED)
|
|
||||||
|
|
||||||
// The object was already moved.
|
|
||||||
new_location = ((MovedObject*) old_location)->new_location;
|
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// This object wasn't moved to
|
|
||||||
// the new heap yet.
|
|
||||||
|
|
||||||
// Get some information.
|
// Get some information.
|
||||||
TypeObject *type = old_location->type;
|
TypeObject *type = old_location->type;
|
||||||
int size = type->size;
|
int size = type->size;
|
||||||
@@ -276,10 +396,10 @@ void Heap_CollectReference(Object **referer, Heap *heap)
|
|||||||
assert((int) sizeof(MovedObject) <= size);
|
assert((int) sizeof(MovedObject) <= size);
|
||||||
((MovedObject*) old_location)->new_location = new_location;
|
((MovedObject*) old_location)->new_location = new_location;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Update the referer
|
if(type->free != NULL)
|
||||||
*referer = new_location;
|
heap->moved_with_destructors += 1;
|
||||||
|
}
|
||||||
|
|
||||||
// Collect the reference to the type.
|
// Collect the reference to the type.
|
||||||
if((Object*) new_location->type != new_location)
|
if((Object*) new_location->type != new_location)
|
||||||
@@ -292,4 +412,8 @@ void Heap_CollectReference(Object **referer, Heap *heap)
|
|||||||
|
|
||||||
// Now collect all of the children.
|
// Now collect all of the children.
|
||||||
Object_WalkReferences(new_location, Heap_CollectReference, heap);
|
Object_WalkReferences(new_location, Heap_CollectReference, heap);
|
||||||
|
|
||||||
|
// Update the referer
|
||||||
|
*referer = new_location;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+6
-2
@@ -85,11 +85,15 @@ static void walk(Object *self, void (*callback)(Object **referer, void *userp),
|
|||||||
|
|
||||||
static void walkexts(Object *self, void (*callback)(void **referer, unsigned int size, void *userp), void *userp)
|
static void walkexts(Object *self, void (*callback)(void **referer, unsigned int size, void *userp), void *userp)
|
||||||
{
|
{
|
||||||
|
assert(self->type == &t_map);
|
||||||
|
|
||||||
MapObject *map = (MapObject*) self;
|
MapObject *map = (MapObject*) self;
|
||||||
|
|
||||||
|
int capacity = calc_capacity(map->mapper_size);
|
||||||
|
|
||||||
callback((void**) &map->mapper, sizeof(int) * map->mapper_size, userp);
|
callback((void**) &map->mapper, sizeof(int) * map->mapper_size, userp);
|
||||||
callback((void**) &map->keys, sizeof(Object) * calc_capacity(map->mapper_size), userp);
|
callback((void**) &map->keys, sizeof(Object*) * capacity, userp);
|
||||||
callback((void**) &map->vals, sizeof(Object) * calc_capacity(map->mapper_size), userp);
|
callback((void**) &map->vals, sizeof(Object*) * capacity, userp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Object *select(Object *self, Object *key, Heap *heap, Error *error)
|
static Object *select(Object *self, Object *key, Heap *heap, Error *error)
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ void* Heap_Malloc (Heap *heap, TypeObject *type, Error *err);
|
|||||||
void* Heap_RawMalloc(Heap *heap, int size, Error *err);
|
void* Heap_RawMalloc(Heap *heap, int size, Error *err);
|
||||||
_Bool Heap_StartCollection(Heap *heap, Error *error);
|
_Bool Heap_StartCollection(Heap *heap, Error *error);
|
||||||
_Bool Heap_StopCollection(Heap *heap);
|
_Bool Heap_StopCollection(Heap *heap);
|
||||||
void Heap_CollectReference(Object **referer, Heap *heap);
|
void Heap_CollectReference(Object **referer, void *heap);
|
||||||
float Heap_GetUsagePercentage(Heap *heap);
|
float Heap_GetUsagePercentage(Heap *heap);
|
||||||
|
|
||||||
const TypeObject* Object_GetType(const Object *obj);
|
const TypeObject* Object_GetType(const Object *obj);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ typedef struct {
|
|||||||
|
|
||||||
static Object *call(Object *self, Object **argv, unsigned int argc, Heap *heap, Error *error);
|
static Object *call(Object *self, Object **argv, unsigned int argc, Heap *heap, Error *error);
|
||||||
static void walk(Object *self, void (*callback)(Object **referer, void *userp), void *userp);
|
static void walk(Object *self, void (*callback)(Object **referer, void *userp), void *userp);
|
||||||
|
static _Bool free_(Object *self, Error *error);
|
||||||
|
|
||||||
static TypeObject t_func = {
|
static TypeObject t_func = {
|
||||||
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
|
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
|
||||||
@@ -21,8 +22,18 @@ static TypeObject t_func = {
|
|||||||
.size = sizeof (FunctionObject),
|
.size = sizeof (FunctionObject),
|
||||||
.call = call,
|
.call = call,
|
||||||
.walk = walk,
|
.walk = walk,
|
||||||
|
.free = free_,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static _Bool free_(Object *self, Error *error)
|
||||||
|
{
|
||||||
|
(void) error;
|
||||||
|
|
||||||
|
FunctionObject *func = (FunctionObject*) self;
|
||||||
|
Executable_Free(func->exe);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static void walk(Object *self, void (*callback)(Object **referer, void *userp), void *userp)
|
static void walk(Object *self, void (*callback)(Object **referer, void *userp), void *userp)
|
||||||
{
|
{
|
||||||
FunctionObject *func = (FunctionObject*) self;
|
FunctionObject *func = (FunctionObject*) self;
|
||||||
@@ -84,6 +95,8 @@ static Object *call(Object *self, Object **argv, unsigned int argc, Heap *heap,
|
|||||||
|
|
||||||
Object *result = run(func->runtime, error, func->exe, func->index, func->closure, argv2, expected_argc);
|
Object *result = run(func->runtime, error, func->exe, func->index, func->closure, argv2, expected_argc);
|
||||||
|
|
||||||
|
// NOTE: Every object reference is invalidated from here.
|
||||||
|
|
||||||
if(argv2 != argv)
|
if(argv2 != argv)
|
||||||
free(argv2);
|
free(argv2);
|
||||||
|
|
||||||
|
|||||||
@@ -84,6 +84,8 @@ static Object *call(Object *self, Object **argv, unsigned int argc, Heap *heap,
|
|||||||
assert(func->callback != NULL);
|
assert(func->callback != NULL);
|
||||||
Object *result = func->callback(func->runtime, argv2, argc2, error);
|
Object *result = func->callback(func->runtime, argv2, argc2, error);
|
||||||
|
|
||||||
|
// NOTE: Every object reference is invalidated from here.
|
||||||
|
|
||||||
if(result == NULL && error->occurred == 0)
|
if(result == NULL && error->occurred == 0)
|
||||||
Error_Report(error, 1, "Native callback returned NULL but didn't report errors");
|
Error_Report(error, 1, "Native callback returned NULL but didn't report errors");
|
||||||
|
|
||||||
|
|||||||
@@ -683,6 +683,7 @@ static _Bool step(Runtime *runtime, Error *error)
|
|||||||
assert(error->occurred == 0);
|
assert(error->occurred == 0);
|
||||||
|
|
||||||
Object *obj = Object_Call(callable, argv, argc, runtime->heap, error);
|
Object *obj = Object_Call(callable, argv, argc, runtime->heap, error);
|
||||||
|
// NOTE: Every local object reference is invalidated from here.
|
||||||
|
|
||||||
if(obj == NULL)
|
if(obj == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user