Revamp wrenCall to work with slots.
Now, you call wrenEnsureSlots() and then wrenSetSlot___() to set up the receiver and arguments before the call. Then wrenCall() is passed a handle to the stub function that makes the call. After that, you can get the result using wrenGetSlot___(). This is a little more verbose to use, but it's more flexible, simpler, and much faster in the VM. The call benchmark is 185% of the previous speed.
This commit is contained in:
+47
-20
@@ -18,7 +18,10 @@ typedef struct sFileRequestData
|
||||
|
||||
static const int stdinDescriptor = 0;
|
||||
|
||||
// Handle to Stdin.onData_(). Called when libuv provides data on stdin.
|
||||
// Handle to the Stdin class object.
|
||||
static WrenValue* stdinClass = NULL;
|
||||
|
||||
// Handle to an onData_() method call. Called when libuv provides data on stdin.
|
||||
static WrenValue* stdinOnData = NULL;
|
||||
|
||||
// The stream used to read from stdin. Initialized on the first read.
|
||||
@@ -33,7 +36,13 @@ static void shutdownStdin()
|
||||
free(stdinStream);
|
||||
stdinStream = NULL;
|
||||
}
|
||||
|
||||
|
||||
if (stdinClass != NULL)
|
||||
{
|
||||
wrenReleaseValue(getVM(), stdinClass);
|
||||
stdinClass = NULL;
|
||||
}
|
||||
|
||||
if (stdinOnData != NULL)
|
||||
{
|
||||
wrenReleaseValue(getVM(), stdinOnData);
|
||||
@@ -121,8 +130,9 @@ static void directoryListCallback(uv_fs_t* request)
|
||||
bufferLength += length + 1;
|
||||
}
|
||||
|
||||
WrenValue* fiber = freeRequest(request);
|
||||
schedulerResumeBytes(fiber, buffer, bufferLength);
|
||||
schedulerResume(freeRequest(request), true);
|
||||
wrenSetSlotBytes(getVM(), 2, buffer, bufferLength);
|
||||
schedulerFinishResume();
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
@@ -161,8 +171,9 @@ static void fileOpenCallback(uv_fs_t* request)
|
||||
if (handleRequestError(request)) return;
|
||||
|
||||
double fd = (double)request->result;
|
||||
WrenValue* fiber = freeRequest(request);
|
||||
schedulerResumeDouble(fiber, fd);
|
||||
schedulerResume(freeRequest(request), true);
|
||||
wrenSetSlotDouble(getVM(), 2, fd);
|
||||
schedulerFinishResume();
|
||||
}
|
||||
|
||||
void fileOpen(WrenVM* vm)
|
||||
@@ -180,8 +191,9 @@ static void fileSizeCallback(uv_fs_t* request)
|
||||
if (handleRequestError(request)) return;
|
||||
|
||||
double size = (double)request->statbuf.st_size;
|
||||
WrenValue* fiber = freeRequest(request);
|
||||
schedulerResumeDouble(fiber, size);
|
||||
schedulerResume(freeRequest(request), true);
|
||||
wrenSetSlotDouble(getVM(), 2, size);
|
||||
schedulerFinishResume();
|
||||
}
|
||||
|
||||
void fileSizePath(WrenVM* vm)
|
||||
@@ -195,8 +207,7 @@ static void fileCloseCallback(uv_fs_t* request)
|
||||
{
|
||||
if (handleRequestError(request)) return;
|
||||
|
||||
WrenValue* fiber = freeRequest(request);
|
||||
schedulerResume(fiber);
|
||||
schedulerResume(freeRequest(request), false);
|
||||
}
|
||||
|
||||
void fileClose(WrenVM* vm)
|
||||
@@ -233,12 +244,12 @@ static void fileReadBytesCallback(uv_fs_t* request)
|
||||
FileRequestData* data = (FileRequestData*)request->data;
|
||||
uv_buf_t buffer = data->buffer;
|
||||
|
||||
WrenValue* fiber = freeRequest(request);
|
||||
|
||||
// TODO: Having to copy the bytes here is a drag. It would be good if Wren's
|
||||
// embedding API supported a way to *give* it bytes that were previously
|
||||
// allocated using Wren's own allocator.
|
||||
schedulerResumeBytes(fiber, buffer.base, buffer.len);
|
||||
schedulerResume(freeRequest(request), true);
|
||||
wrenSetSlotBytes(getVM(), 2, buffer.base, buffer.len);
|
||||
schedulerFinishResume();
|
||||
|
||||
// TODO: Likewise, freeing this after we resume is lame.
|
||||
free(buffer.base);
|
||||
@@ -282,25 +293,41 @@ static void allocCallback(uv_handle_t* handle, size_t suggestedSize,
|
||||
static void stdinReadCallback(uv_stream_t* stream, ssize_t numRead,
|
||||
const uv_buf_t* buffer)
|
||||
{
|
||||
WrenVM* vm = getVM();
|
||||
|
||||
if (stdinClass == NULL)
|
||||
{
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenGetVariable(vm, "io", "Stdin", 0);
|
||||
stdinClass = wrenGetSlotValue(vm, 0);
|
||||
}
|
||||
|
||||
if (stdinOnData == NULL)
|
||||
{
|
||||
stdinOnData = wrenMakeCallHandle(vm, "onData_(_)");
|
||||
}
|
||||
|
||||
// If stdin was closed, send null to let io.wren know.
|
||||
if (numRead == UV_EOF)
|
||||
{
|
||||
wrenCall(getVM(), stdinOnData, NULL, "v", NULL);
|
||||
wrenEnsureSlots(vm, 2);
|
||||
wrenSetSlotValue(vm, 0, stdinClass);
|
||||
wrenSetSlotNull(vm, 1);
|
||||
wrenCall(vm, stdinOnData);
|
||||
|
||||
shutdownStdin();
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Handle other errors.
|
||||
|
||||
if (stdinOnData == NULL)
|
||||
{
|
||||
stdinOnData = wrenGetMethod(getVM(), "io", "Stdin", "onData_(_)");
|
||||
}
|
||||
|
||||
// TODO: Having to copy the bytes here is a drag. It would be good if Wren's
|
||||
// embedding API supported a way to *give* it bytes that were previously
|
||||
// allocated using Wren's own allocator.
|
||||
wrenCall(getVM(), stdinOnData, NULL, "a", buffer->base, numRead);
|
||||
wrenEnsureSlots(vm, 2);
|
||||
wrenSetSlotValue(vm, 0, stdinClass);
|
||||
wrenSetSlotBytes(vm, 1, buffer->base, numRead);
|
||||
wrenCall(vm, stdinOnData);
|
||||
|
||||
// TODO: Likewise, freeing this after we resume is lame.
|
||||
free(buffer->base);
|
||||
|
||||
+38
-33
@@ -7,30 +7,19 @@
|
||||
#include "wren.h"
|
||||
#include "vm.h"
|
||||
|
||||
// A handle to the "Scheduler" class object. Used to call static methods on it.
|
||||
static WrenValue* schedulerClass;
|
||||
|
||||
// This method resumes a fiber that is suspended waiting on an asynchronous
|
||||
// operation. The first resumes it with zero arguments, and the second passes
|
||||
// one.
|
||||
static WrenValue* resume;
|
||||
static WrenValue* resumeWithArg;
|
||||
static WrenValue* resume1;
|
||||
static WrenValue* resume2;
|
||||
static WrenValue* resumeError;
|
||||
|
||||
void schedulerCaptureMethods(WrenVM* vm)
|
||||
static void resume(WrenValue* method)
|
||||
{
|
||||
resume = wrenGetMethod(vm, "scheduler", "Scheduler", "resume_(_)");
|
||||
resumeWithArg = wrenGetMethod(vm, "scheduler", "Scheduler", "resume_(_,_)");
|
||||
resumeError = wrenGetMethod(vm, "scheduler", "Scheduler", "resumeError_(_,_)");
|
||||
}
|
||||
|
||||
static void callResume(WrenValue* resumeMethod, WrenValue* fiber,
|
||||
const char* argTypes, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, argTypes);
|
||||
WrenInterpretResult result = wrenCallVarArgs(getVM(), resumeMethod, NULL,
|
||||
argTypes, args);
|
||||
va_end(args);
|
||||
|
||||
wrenReleaseValue(getVM(), fiber);
|
||||
WrenInterpretResult result = wrenCall(getVM(), method);
|
||||
|
||||
// If a runtime error occurs in response to an async operation and nothing
|
||||
// catches the error in the fiber, then exit the CLI.
|
||||
@@ -41,34 +30,50 @@ static void callResume(WrenValue* resumeMethod, WrenValue* fiber,
|
||||
}
|
||||
}
|
||||
|
||||
void schedulerResume(WrenValue* fiber)
|
||||
void schedulerCaptureMethods(WrenVM* vm)
|
||||
{
|
||||
callResume(resume, fiber, "v", fiber);
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenGetVariable(vm, "scheduler", "Scheduler", 0);
|
||||
schedulerClass = wrenGetSlotValue(vm, 0);
|
||||
|
||||
resume1 = wrenMakeCallHandle(vm, "resume_(_)");
|
||||
resume2 = wrenMakeCallHandle(vm, "resume_(_,_)");
|
||||
resumeError = wrenMakeCallHandle(vm, "resumeError_(_,_)");
|
||||
}
|
||||
|
||||
void schedulerResumeBytes(WrenValue* fiber, const char* bytes, size_t length)
|
||||
void schedulerResume(WrenValue* fiber, bool hasArgument)
|
||||
{
|
||||
callResume(resumeWithArg, fiber, "va", fiber, bytes, length);
|
||||
WrenVM* vm = getVM();
|
||||
wrenEnsureSlots(vm, 2 + (hasArgument ? 1 : 0));
|
||||
wrenSetSlotValue(vm, 0, schedulerClass);
|
||||
wrenSetSlotValue(vm, 1, fiber);
|
||||
wrenReleaseValue(vm, fiber);
|
||||
|
||||
// If we don't need to wait for an argument to be stored on the stack, resume
|
||||
// it now.
|
||||
if (!hasArgument) resume(resume1);
|
||||
}
|
||||
|
||||
void schedulerResumeDouble(WrenValue* fiber, double value)
|
||||
void schedulerFinishResume()
|
||||
{
|
||||
callResume(resumeWithArg, fiber, "vd", fiber, value);
|
||||
}
|
||||
|
||||
void schedulerResumeString(WrenValue* fiber, const char* text)
|
||||
{
|
||||
callResume(resumeWithArg, fiber, "vs", fiber, text);
|
||||
resume(resume2);
|
||||
}
|
||||
|
||||
void schedulerResumeError(WrenValue* fiber, const char* error)
|
||||
{
|
||||
callResume(resumeError, fiber, "vs", fiber, error);
|
||||
schedulerResume(fiber, true);
|
||||
wrenSetSlotString(getVM(), 2, error);
|
||||
resume(resumeError);
|
||||
}
|
||||
|
||||
void schedulerShutdown()
|
||||
{
|
||||
if (resume != NULL) wrenReleaseValue(getVM(), resume);
|
||||
if (resumeWithArg != NULL) wrenReleaseValue(getVM(), resumeWithArg);
|
||||
if (resumeError != NULL) wrenReleaseValue(getVM(), resumeError);
|
||||
// If the module was never loaded, we don't have anything to release.
|
||||
if (schedulerClass == NULL) return;
|
||||
|
||||
WrenVM* vm = getVM();
|
||||
wrenReleaseValue(vm, schedulerClass);
|
||||
wrenReleaseValue(vm, resume1);
|
||||
wrenReleaseValue(vm, resume2);
|
||||
wrenReleaseValue(vm, resumeError);
|
||||
}
|
||||
|
||||
+10
-4
@@ -3,10 +3,16 @@
|
||||
|
||||
#include "wren.h"
|
||||
|
||||
void schedulerResume(WrenValue* fiber);
|
||||
void schedulerResumeBytes(WrenValue* fiber, const char* bytes, size_t length);
|
||||
void schedulerResumeDouble(WrenValue* fiber, double value);
|
||||
void schedulerResumeString(WrenValue* fiber, const char* text);
|
||||
// Sets up the API stack to call one of the resume methods on Scheduler.
|
||||
//
|
||||
// If [hasArgument] is false, this just sets up the stack to have another
|
||||
// argument stored in slot 2 and returns. The module must store the argument
|
||||
// on the stack and then call [schedulerFinishResume] to complete the call.
|
||||
//
|
||||
// Otherwise, the call resumes immediately. Releases [fiber] when called.
|
||||
void schedulerResume(WrenValue* fiber, bool hasArgument);
|
||||
|
||||
void schedulerFinishResume();
|
||||
void schedulerResumeError(WrenValue* fiber, const char* error);
|
||||
|
||||
void schedulerShutdown();
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ static void timerCallback(uv_timer_t* handle)
|
||||
uv_close((uv_handle_t*)handle, timerCloseCallback);
|
||||
|
||||
// Run the fiber that was sleeping.
|
||||
schedulerResume(fiber);
|
||||
schedulerResume(fiber, false);
|
||||
}
|
||||
|
||||
void timerStartTimer(WrenVM* vm)
|
||||
|
||||
Reference in New Issue
Block a user