diff --git a/src/wren_compiler.c b/src/wren_compiler.c index b1c250c3..d34dcb24 100644 --- a/src/wren_compiler.c +++ b/src/wren_compiler.c @@ -1542,7 +1542,8 @@ ObjFn* wrenCompile(WrenVM* vm, const char* source) Compiler compiler; initCompiler(&compiler, &parser, NULL, 0); - pinObj(vm, (Obj*)compiler.fn); + PinnedObj pinned; + pinObj(vm, (Obj*)compiler.fn, &pinned); for (;;) { @@ -1563,7 +1564,7 @@ ObjFn* wrenCompile(WrenVM* vm, const char* source) emit(&compiler, CODE_END); - unpinObj(vm, (Obj*)compiler.fn); + unpinObj(vm); return parser.hasError ? NULL : compiler.fn; } diff --git a/src/wren_value.c b/src/wren_value.c index bbbfe227..ce7e7c71 100644 --- a/src/wren_value.c +++ b/src/wren_value.c @@ -53,12 +53,13 @@ ObjClass* wrenNewClass(WrenVM* vm, ObjClass* superclass, int numFields) ObjClass* metaclass = newClass(vm, NULL, vm->classClass, 0); // Make sure it isn't collected when we allocate the metaclass. - pinObj(vm, (Obj*)metaclass); + PinnedObj pinned; + pinObj(vm, (Obj*)metaclass, &pinned); ObjClass* classObj = newClass(vm, metaclass, superclass, numFields); classObj->numFields = numFields; - unpinObj(vm, (Obj*)metaclass); + unpinObj(vm); return classObj; } diff --git a/src/wren_vm.c b/src/wren_vm.c index 84f48e71..61cdaab9 100644 --- a/src/wren_vm.c +++ b/src/wren_vm.c @@ -23,7 +23,10 @@ WrenVM* wrenNewVM(WrenReallocateFn reallocateFn) // TODO(bob): Make this configurable. vm->nextGC = 1024 * 1024 * 10; + vm->first = NULL; + vm->pinned = NULL; + // Clear out the global variables. This ensures they are NULL before being // initialized in case we do a garbage collection before one gets initialized. for (int i = 0; i < MAX_SYMBOLS; i++) @@ -284,9 +287,11 @@ static void collectGarbage(WrenVM* vm) } // Pinned objects. - for (int j = 0; j < vm->numPinned; j++) + PinnedObj* pinned = vm->pinned; + while (pinned != NULL) { - markObj(vm->pinned[j]); + markObj(pinned->obj); + pinned = pinned->previous; } // Stack functions. @@ -1086,14 +1091,14 @@ void callFunction(Fiber* fiber, ObjFn* fn, int numArgs) fiber->numFrames++; } -void pinObj(WrenVM* vm, Obj* obj) +void pinObj(WrenVM* vm, Obj* obj, PinnedObj* pinned) { - ASSERT(vm->numPinned < MAX_PINNED - 1, "Too many pinned objects."); - vm->pinned[vm->numPinned++] = obj; + pinned->obj = obj; + pinned->previous = vm->pinned; + vm->pinned = pinned; } -void unpinObj(WrenVM* vm, Obj* obj) +void unpinObj(WrenVM* vm) { - ASSERT(vm->pinned[vm->numPinned - 1] == obj, "Unpinning out of stack order."); - vm->numPinned--; + vm->pinned = vm->pinned->previous; } diff --git a/src/wren_vm.h b/src/wren_vm.h index 5d4df766..221db8d3 100644 --- a/src/wren_vm.h +++ b/src/wren_vm.h @@ -7,7 +7,6 @@ // TODO(bob): Make these externally controllable. #define STACK_SIZE 1024 #define MAX_CALL_FRAMES 256 -#define MAX_PINNED 16 typedef enum { @@ -117,6 +116,23 @@ typedef struct int count; } SymbolTable; +// A pinned object is an Obj that has been temporarily made an explicit GC root. +// This is for temporary or new objects that are not otherwise reachable but +// should not be collected. +// +// They are organized as linked list of these objects stored on the stack. The +// WrenVM has a pointer to the head of the list and walks it if a collection +// occurs. This implies that pinned objects need to have stack semantics: only +// the most recently pinned object can be unpinned. +typedef struct sPinnedObj +{ + // The pinned object. + Obj* obj; + + // The next pinned object. + struct sPinnedObj* previous; +} PinnedObj; + struct WrenVM { SymbolTable methods; @@ -151,13 +167,9 @@ struct WrenVM // The first object in the linked list of all currently allocated objects. Obj* first; - // How many pinned objects are in the stack. - int numPinned; - - // The stack "pinned" objects. These are temporary explicit GC roots so that - // the collector can find them before they are reachable through a normal - // root. - Obj* pinned[MAX_PINNED]; + // The head of the list of pinned objects. Will be `NULL` if nothing is + // pinned. + PinnedObj* pinned; }; typedef struct @@ -221,10 +233,11 @@ Value interpret(WrenVM* vm, ObjFn* fn); // arguments (including the receiver) to be on the top of the stack already. void callFunction(Fiber* fiber, ObjFn* fn, int numArgs); -// Mark [obj] as a GC root so that it doesn't get collected. -void pinObj(WrenVM* vm, Obj* obj); +// Mark [obj] as a GC root so that it doesn't get collected. Initializes +// [pinned], which must be then passed to [unpinObj]. +void pinObj(WrenVM* vm, Obj* obj, PinnedObj* pinned); -// Unmark [obj] as a GC root so that it doesn't get collected. -void unpinObj(WrenVM* vm, Obj* obj); +// Remove the most recently pinned object from the list of pinned GC roots. +void unpinObj(WrenVM* vm); #endif