Use a fiber for a finalizer's foreign stack.

This commit is contained in:
Bob Nystrom
2015-12-27 21:43:08 -08:00
parent 9512ce74c3
commit a09892de32
4 changed files with 26 additions and 20 deletions

View File

@ -48,7 +48,7 @@ test: debug
benchmark: release
@ $(MAKE) -f util/wren.mk test
@ ./util/benchmark.py $(suite)
@ ./util/benchmark.py -l wren $(suite)
# Generate the Wren site.
docs:

View File

@ -152,7 +152,9 @@ ObjFiber* wrenNewFiber(WrenVM* vm, Obj* fn)
// Add one slot for the unused implicit receiver slot that the compiler
// assumes all functions have.
int stackCapacity = wrenPowerOf2Ceil(wrenUpwrapClosure(fn)->maxSlots + 1);
int stackCapacity = fn == NULL
? 1
: wrenPowerOf2Ceil(wrenUpwrapClosure(fn)->maxSlots + 1);
Value* stack = ALLOCATE_ARRAY(vm, Value, stackCapacity);
ObjFiber* fiber = ALLOCATE(vm, ObjFiber);
@ -174,10 +176,10 @@ void wrenResetFiber(WrenVM* vm, ObjFiber* fiber, Obj* fn)
fiber->caller = NULL;
fiber->error = NULL_VAL;
fiber->callerIsTrying = false;
fiber->numFrames = 0;
// Initialize the first call frame.
fiber->numFrames = 0;
wrenAppendCallFrame(vm, fiber, fn, fiber->stack);
if (fn != NULL) wrenAppendCallFrame(vm, fiber, fn, fiber->stack);
}
ObjForeign* wrenNewForeign(WrenVM* vm, ObjClass* classObj, size_t size)

View File

@ -65,6 +65,8 @@ WrenVM* wrenNewVM(WrenConfiguration* config)
vm->modules = wrenNewMap(vm);
wrenInitializeCore(vm);
vm->finalizerFiber = wrenNewFiber(vm, NULL);
// TODO: Lazy load these.
#if WREN_OPT_META
@ -137,8 +139,9 @@ void wrenCollectGarbage(WrenVM* vm)
wrenGrayObj(vm, vm->tempRoots[i]);
}
// The current fiber.
// The rooted fibers.
wrenGrayObj(vm, (Obj*)vm->fiber);
wrenGrayObj(vm, (Obj*)vm->finalizerFiber);
// The value handles.
for (WrenValue* value = vm->valueHandles;
@ -740,10 +743,16 @@ void wrenFinalizeForeign(WrenVM* vm, ObjForeign* foreign)
ASSERT(method->type == METHOD_FOREIGN, "Finalizer should be foreign.");
// Pass the foreign object to the finalizer.
Value slot = OBJ_VAL(foreign);
vm->foreignStackStart = &slot;
*vm->finalizerFiber->stackTop++ = OBJ_VAL(foreign);
ObjFiber* previousFiber = vm->fiber;
vm->fiber = vm->finalizerFiber;
vm->foreignStackStart = vm->finalizerFiber->stack;
method->fn.foreign(vm);
vm->fiber = previousFiber;
vm->foreignStackStart = NULL;
wrenResetFiber(vm, vm->finalizerFiber, NULL);
}
// The main bytecode interpreter loop. This is where the magic happens. It is
@ -1637,28 +1646,17 @@ void wrenPopRoot(WrenVM* vm)
vm->numTempRoots--;
}
// Returns true if the VM is in a foreign method that's a finalizer.
//
// Finalizers don't run in the context of a fiber and have a single magic stack
// slot, so need to be handled a little specially.
static bool isInFinalizer(WrenVM* vm)
{
return vm->fiber == NULL ||
vm->foreignStackStart < vm->fiber->stack ||
vm->foreignStackStart > vm->fiber->stackTop;
}
int wrenGetSlotCount(WrenVM* vm)
{
ASSERT(vm->foreignStackStart != NULL, "Must be in foreign call.");
if (isInFinalizer(vm)) return 1;
return (int)(vm->fiber->stackTop - vm->foreignStackStart);
}
void wrenEnsureSlots(WrenVM* vm, int numSlots)
{
ASSERT(!isInFinalizer(vm), "Cannot grow the stack in a finalizer.");
ASSERT(vm->fiber != vm->finalizerFiber,
"Cannot grow the stack in a finalizer.");
int currentSize = (int)(vm->fiber->stackTop - vm->foreignStackStart);
if (currentSize >= numSlots) return;

View File

@ -102,6 +102,12 @@ struct WrenVM
// There is a single global symbol table for all method names on all classes.
// Method calls are dispatched directly by index in this table.
SymbolTable methodNames;
// A special fiber whose stack is used for communicating with foreign
// finalizer methods. Finalizers run during GC, so we don't want to do any
// allocation in order to execute one, so we set up this fiber ahead of time
// to ensure it's available.
ObjFiber* finalizerFiber;
};
// A generic allocation function that handles all explicit memory management.