mirror of
https://github.com/wren-lang/wren.git
synced 2026-01-10 13:48:40 +01:00
Use a fiber for a finalizer's foreign stack.
This commit is contained in:
2
Makefile
2
Makefile
@ -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:
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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.
|
||||
|
||||
Reference in New Issue
Block a user