add preemptive scheduler
This commit is contained in:
parent
a1d8e52a1a
commit
99c6ebded3
|
@ -22,6 +22,8 @@ set(SOURCES
|
|||
src/ui.c
|
||||
src/battery.c
|
||||
src/syscalls.S
|
||||
src/job.c
|
||||
src/wren_utils.c
|
||||
)
|
||||
set(SOURCES_WREN
|
||||
wren/src/wren_compiler.c
|
||||
|
@ -34,6 +36,9 @@ set(SOURCES_WREN
|
|||
wren/src/wren_opt_meta.c
|
||||
wren/src/wren_opt_random.c
|
||||
)
|
||||
set(SOURCES_PRINTF
|
||||
src/printf.c
|
||||
)
|
||||
|
||||
# shared assets
|
||||
set(ASSETS
|
||||
|
@ -79,23 +84,37 @@ set(FLAGS
|
|||
# -O2: good speed/size tradeoff
|
||||
# -O3: gotta go fast
|
||||
-Os
|
||||
-m4a-nofpu
|
||||
-flto -ffat-lto-objects
|
||||
)
|
||||
set(FLAGS_WREN
|
||||
-std=c99
|
||||
-std=c11
|
||||
-Os
|
||||
-m4a-nofpu
|
||||
-flto -ffat-lto-objects
|
||||
)
|
||||
set(FLAGS_PRINTF
|
||||
-std=c11
|
||||
-Ofast
|
||||
-m4a-nofpu
|
||||
-flto -ffat-lto-objects
|
||||
)
|
||||
|
||||
set(CMAKE_ASM_FLAGS "-x assembler-with-cpp")
|
||||
add_link_options(-flto)
|
||||
|
||||
add_library(Printf ${SOURCES_PRINTF})
|
||||
target_compile_options(Printf PRIVATE ${FLAGS_PRINTF})
|
||||
|
||||
add_library(Wren ${SOURCES_WREN})
|
||||
target_compile_options(Wren PRIVATE ${FLAGS_WREN})
|
||||
target_link_libraries(Wren Printf Gint::Gint)
|
||||
|
||||
fxconv_declare_assets(${ASSETS} ${ASSETS_fx} ${ASSETS_cg} WITH_METADATA)
|
||||
add_executable(Main ${SOURCES} ${ASSETS} ${ASSETS_${FXSDK_PLATFORM}})
|
||||
target_compile_options(Main PRIVATE ${FLAGS})
|
||||
|
||||
target_link_libraries(Main Wren Gint::Gint)
|
||||
target_link_libraries(Main Printf Wren Gint::Gint)
|
||||
|
||||
if("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G)
|
||||
generate_g1a(
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
#include "job.h"
|
||||
|
||||
#include <gint/kmalloc.h>
|
||||
|
||||
#include "term.h"
|
||||
|
||||
extern jmp_buf sched_ctxbuf;
|
||||
|
||||
WrenVM *job_vms[UNS_MAX_JOBS] = {0};
|
||||
void *job_stacks[UNS_MAX_JOBS] = {0};
|
||||
int current_job_id = -1;
|
||||
|
||||
static void *job_start_(const uint32_t entry_point) {
|
||||
term_kprint("allocate new stack...");
|
||||
static const int job_stack_size = 64 * 1024; // 64 kiB stack
|
||||
const uint32_t child_stack = (uint32_t)kmalloc(job_stack_size, NULL);
|
||||
const uint32_t new_sp = ((child_stack + 3) >> 2 << 2) + ((job_stack_size + 3) >> 2 << 2);
|
||||
term_kprintf("new_bp=0x%08x", child_stack);
|
||||
term_kprintf("new_sp=0x%08x", new_sp);
|
||||
|
||||
// get a copy of the current context
|
||||
jmp_buf ctx;
|
||||
setjmp(ctx);
|
||||
|
||||
// transform it to the hidden representation so we can modify it
|
||||
struct __jmp_buf *ctx_ptr = &ctx[0];
|
||||
|
||||
ctx_ptr->reg[7] = new_sp; // change stack pointer
|
||||
ctx_ptr->pr = entry_point; // change pc backup
|
||||
|
||||
const int ret = setjmp(sched_ctxbuf);
|
||||
if (ret == 0)
|
||||
longjmp(ctx, 1); // apply changes, run job until it yields a first time
|
||||
else
|
||||
return (void *)child_stack; // then return the stack descriptor (not "stack pointer"!) for later free
|
||||
}
|
||||
|
||||
int job_start(uint32_t entry_point) {
|
||||
for (int i = 0; i < UNS_MAX_JOBS; i++) {
|
||||
if (job_vms[i] == NULL) {
|
||||
current_job_id = i;
|
||||
job_stacks[i] = job_start_(entry_point);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
// no slot found
|
||||
return -1;
|
||||
}
|
||||
|
||||
void job_resume(int job_id) {
|
||||
wrenResume(job_vms[job_id]); // longjmp(vm->ctxbuf, 1);
|
||||
}
|
||||
|
||||
void job_free(int job_id) {
|
||||
wrenFreeVM(job_vms[job_id]);
|
||||
job_vms[job_id] = NULL;
|
||||
|
||||
kfree(job_stacks[job_id]);
|
||||
job_stacks[job_id] = NULL;
|
||||
}
|
||||
|
||||
void next_job(void) {
|
||||
for (int i = 1; i < UNS_MAX_JOBS + 1; i++) {
|
||||
const int candidate = (current_job_id + i) % UNS_MAX_JOBS;
|
||||
|
||||
if (job_stacks[candidate] != NULL) {
|
||||
current_job_id = candidate;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
current_job_id = -1;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef UNS_JOB_H
|
||||
#define UNS_JOB_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <wren.h>
|
||||
|
||||
#define UNS_MAX_JOBS 128
|
||||
extern WrenVM *job_vms[UNS_MAX_JOBS];
|
||||
extern void *job_stacks[UNS_MAX_JOBS];
|
||||
extern int current_job_id;
|
||||
|
||||
int job_start(uint32_t entry_point);
|
||||
void job_resume(int job_id);
|
||||
void job_free(int job_id);
|
||||
|
||||
void next_job(void);
|
||||
|
||||
#endif // #ifndef UNS_JOB_H
|
180
src/main.c
180
src/main.c
|
@ -1,4 +1,6 @@
|
|||
#include <stdio.h>
|
||||
#include <printf.h>
|
||||
#include <setjmp.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <gint/cpu.h>
|
||||
#include <gint/display.h>
|
||||
|
@ -9,57 +11,33 @@
|
|||
#include <gint/mpu/power.h>
|
||||
#include <gint/timer.h>
|
||||
|
||||
#include <wren.h>
|
||||
|
||||
#include "battery.h"
|
||||
#include "job.h"
|
||||
#include "term.h"
|
||||
#include "ui.h"
|
||||
|
||||
static void wn_write(WrenVM __attribute__((unused)) * vm, const char *text) { term_print(text); }
|
||||
static void wn_error(WrenVM __attribute__((unused)) * vm, WrenErrorType errorType, const char *module, const int line,
|
||||
const char *msg) {
|
||||
switch (errorType) {
|
||||
case WREN_ERROR_COMPILE:
|
||||
term_printf("[%s line %d] [Error] %s\n", module, line, msg);
|
||||
break;
|
||||
case WREN_ERROR_STACK_TRACE:
|
||||
term_eprintf("[%s line %d] in %s\n", module, line, msg);
|
||||
break;
|
||||
case WREN_ERROR_RUNTIME:
|
||||
term_eprintf("[Runtime Error] %s\n", msg);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void *wn_reallocate(void *memory, size_t newSize, __attribute__((unused)) void *userData) {
|
||||
if (memory == NULL && newSize == 0)
|
||||
return NULL;
|
||||
|
||||
if (memory == NULL)
|
||||
return kmalloc(newSize, "_uram");
|
||||
|
||||
if (newSize == 0) {
|
||||
kfree(memory);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return krealloc(memory, newSize);
|
||||
}
|
||||
#include "wren_utils.h"
|
||||
|
||||
static int tick_ctr = 0;
|
||||
static int cycle_ctr = 0;
|
||||
static int shift_state = 0;
|
||||
static int alpha_state = 0;
|
||||
static int off_state = 0;
|
||||
|
||||
static int timer_redraw;
|
||||
static volatile int must_redraw;
|
||||
static int timer_redraw;
|
||||
static int callback_redraw(void) {
|
||||
must_redraw = 1;
|
||||
return TIMER_CONTINUE;
|
||||
}
|
||||
|
||||
volatile int must_yield;
|
||||
jmp_buf sched_ctxbuf;
|
||||
static int timer_sched;
|
||||
static int callback_sched(void) {
|
||||
must_yield = 1;
|
||||
return TIMER_CONTINUE;
|
||||
}
|
||||
|
||||
static void check_keyevents(void) {
|
||||
static uint16_t backlight_save;
|
||||
|
||||
|
@ -118,33 +96,91 @@ static void check_keyevents(void) {
|
|||
}
|
||||
}
|
||||
|
||||
int main(__attribute__((unused)) int isappli, __attribute__((unused)) int optnum) {
|
||||
__attribute__((noreturn)) static void run_vm_1(void) {
|
||||
WrenConfiguration config;
|
||||
wrenInitConfiguration(&config);
|
||||
config.reallocateFn = &wn_reallocate;
|
||||
config.initialHeapSize = 1024 * 4; // 4kiB
|
||||
config.minHeapSize = 1024; // 1kiB
|
||||
config.heapGrowthPercent = 20;
|
||||
config.writeFn = &wn_write;
|
||||
config.errorFn = &wn_error;
|
||||
init_wren_config(&config);
|
||||
|
||||
term_kprint("create wren VM...");
|
||||
must_yield = 0;
|
||||
WrenVM *vm = wrenNewVM(&config);
|
||||
|
||||
job_vms[current_job_id] = vm;
|
||||
|
||||
term_kprint("start interpreter...");
|
||||
must_yield = 1; // yield ASAP
|
||||
wrenInterpret(vm, "my_module",
|
||||
"System.print(\"I am running in a VM!\")\n"
|
||||
"System.trigger_error_from_vm()");
|
||||
wrenFreeVM(vm);
|
||||
"System.print(\"inner start\")\n"
|
||||
"var i = 0\n"
|
||||
"while (i < 30000) {\n"
|
||||
" if ((i % 500) == 0) {\n"
|
||||
" System.print(\"vm 1: i=%(i)\")\n"
|
||||
" }\n"
|
||||
" i = i + 1\n"
|
||||
"}\n"
|
||||
"System.print(\"inner end\")\n");
|
||||
term_kprint("interpreter returned");
|
||||
|
||||
// never return !!!
|
||||
// longjmp to scheduler instead
|
||||
// return value of 2 means job exit
|
||||
longjmp(sched_ctxbuf, 2);
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) static void run_vm_2(void) {
|
||||
WrenConfiguration config;
|
||||
init_wren_config(&config);
|
||||
|
||||
term_kprint("create wren VM...");
|
||||
must_yield = 0;
|
||||
WrenVM *vm = wrenNewVM(&config);
|
||||
|
||||
job_vms[current_job_id] = vm;
|
||||
|
||||
term_kprint("start interpreter...");
|
||||
must_yield = 1; // yield ASAP
|
||||
wrenInterpret(vm, "my_module_2",
|
||||
"System.print(\"inner start\")\n"
|
||||
"var i = 0\n"
|
||||
"while (i < 30000) {\n"
|
||||
" if ((i % 500) == 0) {\n"
|
||||
" System.print(\"vm 2: i=%(i)\")\n"
|
||||
" }\n"
|
||||
" i = i + 1\n"
|
||||
"}\n"
|
||||
"System.print(\"inner end\")\n");
|
||||
term_kprint("interpreter returned");
|
||||
|
||||
// never return !!!
|
||||
// longjmp to scheduler instead
|
||||
// return value of 2 means job exit
|
||||
longjmp(sched_ctxbuf, 2);
|
||||
}
|
||||
|
||||
int main(int isappli, int optnum) {
|
||||
term_kprintf("main(%d, %d)", isappli, optnum);
|
||||
|
||||
timer_sched = timer_configure(TIMER_ANY, 10 * 1000, GINT_CALL(callback_sched)); // 100 Hz <=> 10 ms
|
||||
|
||||
timer_redraw = timer_configure(TIMER_ANY, 31250, GINT_CALL(callback_redraw)); // 32 Hz
|
||||
timer_start(timer_redraw);
|
||||
|
||||
// start a few jobs
|
||||
job_start((uint32_t)run_vm_1);
|
||||
job_start((uint32_t)run_vm_2);
|
||||
|
||||
while (1) {
|
||||
// system routine
|
||||
|
||||
check_keyevents();
|
||||
if (off_state) {
|
||||
sleep();
|
||||
continue;
|
||||
}
|
||||
|
||||
tick_ctr++;
|
||||
|
||||
if (must_redraw) {
|
||||
set_statusbar(tick_ctr, shift_state, alpha_state, get_battery_voltage());
|
||||
set_statusbar(tick_ctr, cycle_ctr, shift_state, alpha_state, get_battery_voltage());
|
||||
set_menubar();
|
||||
|
||||
dclear(C_BLACK);
|
||||
|
@ -154,8 +190,52 @@ int main(__attribute__((unused)) int isappli, __attribute__((unused)) int optnum
|
|||
must_redraw = 0;
|
||||
}
|
||||
|
||||
tick_ctr++;
|
||||
// scheduler routine
|
||||
|
||||
next_job();
|
||||
if (current_job_id < 0) {
|
||||
term_kprint("all jobs ended");
|
||||
break;
|
||||
}
|
||||
|
||||
// first, backup current scheduler context
|
||||
const int ret = setjmp(sched_ctxbuf);
|
||||
if (ret == 0) {
|
||||
// pause timer during sheduler operations, we restart it before resuming a job
|
||||
timer_pause(timer_sched);
|
||||
cycle_ctr++;
|
||||
|
||||
// resume job
|
||||
timer_start(timer_sched);
|
||||
must_yield = 0;
|
||||
job_resume(current_job_id);
|
||||
} else if (ret == 1) {
|
||||
// term_kprintf("job(%d) yield", current_job_id);
|
||||
continue;
|
||||
} else if (ret == 2) {
|
||||
term_kprintf("job(%d) exit", current_job_id);
|
||||
job_free(current_job_id);
|
||||
} else {
|
||||
__builtin_unreachable();
|
||||
term_kprint("SHOULD NOT BE REACHED!!!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
term_eprint("SCHEDULER EXIT");
|
||||
|
||||
set_statusbar(tick_ctr, cycle_ctr, shift_state, alpha_state, get_battery_voltage());
|
||||
set_menubar();
|
||||
|
||||
dclear(C_BLACK);
|
||||
tgrid_display();
|
||||
dupdate();
|
||||
|
||||
while (1) {
|
||||
getkey();
|
||||
}
|
||||
|
||||
__builtin_unreachable();
|
||||
|
||||
return 1;
|
||||
}
|
13
src/term.c
13
src/term.c
|
@ -1,12 +1,11 @@
|
|||
#include "term.h"
|
||||
|
||||
#include <gint/cpu.h>
|
||||
#include <gint/display.h>
|
||||
#include <gint/rtc.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <printf.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
extern font_t uf5x7;
|
||||
|
@ -148,7 +147,7 @@ int term_printf(const char *restrict format, ...) {
|
|||
va_start(argp, format);
|
||||
|
||||
char buf[1024];
|
||||
const int n = vsprintf(buf, format, argp);
|
||||
const int n = vsnprintf(buf, sizeof(buf), format, argp);
|
||||
term_print(buf);
|
||||
|
||||
va_end(argp);
|
||||
|
@ -161,7 +160,7 @@ int term_eprintf(const char *restrict format, ...) {
|
|||
va_start(argp, format);
|
||||
|
||||
char buf[1024];
|
||||
const int n = vsprintf(buf, format, argp);
|
||||
const int n = vsnprintf(buf, sizeof(buf), format, argp);
|
||||
term_eprint(buf);
|
||||
|
||||
va_end(argp);
|
||||
|
@ -169,8 +168,6 @@ int term_eprintf(const char *restrict format, ...) {
|
|||
}
|
||||
|
||||
int term_kprint(const char *str) {
|
||||
cpu_atomic_start();
|
||||
|
||||
int t = rtc_ticks();
|
||||
|
||||
char kstr[256];
|
||||
|
@ -178,8 +175,6 @@ int term_kprint(const char *str) {
|
|||
|
||||
const int ret = term_print_opt(kstr, C_RED | C_GREEN, C_BLACK);
|
||||
|
||||
cpu_atomic_end();
|
||||
|
||||
return ret;
|
||||
}
|
||||
int term_kprintf(const char *restrict format, ...) {
|
||||
|
@ -187,7 +182,7 @@ int term_kprintf(const char *restrict format, ...) {
|
|||
va_start(argp, format);
|
||||
|
||||
char buf[1024];
|
||||
const int n = vsprintf(buf, format, argp);
|
||||
const int n = vsnprintf(buf, sizeof(buf), format, argp);
|
||||
term_kprint(buf);
|
||||
|
||||
va_end(argp);
|
||||
|
|
3
src/ui.c
3
src/ui.c
|
@ -27,7 +27,8 @@ void set_statusbar(int tick_ctr, int cycle_ctr, int shift_state, int alpha_state
|
|||
|
||||
// then add actutal text
|
||||
char statusbar[UNS_TERM_COLS + 1];
|
||||
sprintf(statusbar, "%s%s t=%dk c=%d bat=%.2fV %s", shift_symbol, alpha_symbol, tick_ctr / 1000, cycle_ctr, (float)battery / 100, now);
|
||||
sprintf(statusbar, "%s%s t=%dk c=%d bat=%.2fV %s", shift_symbol, alpha_symbol, tick_ctr / 1000, cycle_ctr,
|
||||
(float)battery / 100, now);
|
||||
term_writeat(0, 0, C_BLACK, C_GREEN, statusbar);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
#include "wren_utils.h"
|
||||
|
||||
#include <gint/kmalloc.h>
|
||||
|
||||
#include "term.h"
|
||||
|
||||
static void wn_write(WrenVM __attribute__((unused)) * vm, const char *text) { term_print(text); }
|
||||
static void wn_error(WrenVM __attribute__((unused)) * vm, WrenErrorType errorType, const char *module, const int line,
|
||||
const char *msg) {
|
||||
switch (errorType) {
|
||||
case WREN_ERROR_COMPILE:
|
||||
term_printf("[%s line %d] [Error] %s\n", module, line, msg);
|
||||
break;
|
||||
case WREN_ERROR_STACK_TRACE:
|
||||
term_eprintf("[%s line %d] in %s\n", module, line, msg);
|
||||
break;
|
||||
case WREN_ERROR_RUNTIME:
|
||||
term_eprintf("[Runtime Error] %s\n", msg);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void *wn_reallocate(void *memory, size_t newSize, __attribute__((unused)) void *userData) {
|
||||
return krealloc(memory, newSize);
|
||||
}
|
||||
|
||||
void init_wren_config(WrenConfiguration *config) {
|
||||
wrenInitConfiguration(config);
|
||||
|
||||
config->reallocateFn = &wn_reallocate;
|
||||
config->initialHeapSize = 16 * 1024; // 16 kiB
|
||||
config->minHeapSize = 1024; // 1 kiB
|
||||
config->heapGrowthPercent = 50;
|
||||
config->writeFn = &wn_write;
|
||||
config->errorFn = &wn_error;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef UNS_WREN_UTILS_H
|
||||
#define UNS_WREN_UTILS_H
|
||||
|
||||
#include <wren.h>
|
||||
|
||||
void init_wren_config(WrenConfiguration *config);
|
||||
|
||||
#endif // #ifndef UNS_WREN_UTILS_H
|
|
@ -5,6 +5,8 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <setjmp.h>
|
||||
|
||||
// The Wren semantic version number components.
|
||||
#define WREN_VERSION_MAJOR 0
|
||||
#define WREN_VERSION_MINOR 4
|
||||
|
@ -33,6 +35,10 @@
|
|||
// here.
|
||||
typedef struct WrenVM WrenVM;
|
||||
|
||||
// restore execution after a yield
|
||||
__attribute__((noreturn))
|
||||
void wrenResume(WrenVM* vm);
|
||||
|
||||
// A handle to a Wren object.
|
||||
//
|
||||
// This lets code outside of the VM hold a persistent reference to an object.
|
||||
|
@ -274,6 +280,7 @@ typedef struct
|
|||
typedef enum
|
||||
{
|
||||
WREN_RESULT_SUCCESS,
|
||||
WREN_RESULT_YIELD,
|
||||
WREN_RESULT_COMPILE_ERROR,
|
||||
WREN_RESULT_RUNTIME_ERROR
|
||||
} WrenInterpretResult;
|
||||
|
|
|
@ -154,7 +154,7 @@
|
|||
// Assertions add significant overhead, so are only enabled in debug builds.
|
||||
#ifdef DEBUG
|
||||
|
||||
#include <stdio.h>
|
||||
#include <printf.h>
|
||||
|
||||
#define ASSERT(condition, message) \
|
||||
do \
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <printf.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "wren_common.h"
|
||||
|
@ -429,7 +429,7 @@ static void printError(Parser* parser, int line, const char* label,
|
|||
// Format the label and message.
|
||||
char message[ERROR_MESSAGE_SIZE];
|
||||
int length = sprintf(message, "%s: ", label);
|
||||
length += vsprintf(message + length, format, args);
|
||||
length += vsnprintf(message + length, ERROR_MESSAGE_SIZE - length, format, args);
|
||||
ASSERT(length < ERROR_MESSAGE_SIZE, "Error should not exceed buffer.");
|
||||
|
||||
ObjString* module = parser->module->name;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include <stdio.h>
|
||||
#include <printf.h>
|
||||
#include <term.h>
|
||||
#undef printf
|
||||
#define printf(...) term_printf(__VA_ARGS__)
|
||||
|
||||
#include "wren_debug.h"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <printf.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "wren.h"
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#include "wren_primitive.h"
|
||||
#include "wren_vm.h"
|
||||
|
||||
#include <term.h>
|
||||
|
||||
#if WREN_OPT_META
|
||||
#include "wren_opt_meta.h"
|
||||
#endif
|
||||
|
@ -18,7 +20,7 @@
|
|||
|
||||
#if WREN_DEBUG_TRACE_MEMORY || WREN_DEBUG_TRACE_GC
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <printf.h>
|
||||
#endif
|
||||
|
||||
// The behavior of realloc() when the size is 0 is implementation defined. It
|
||||
|
@ -303,7 +305,7 @@ static void closeUpvalues(ObjFiber* fiber, Value* last)
|
|||
// Looks up a foreign method in [moduleName] on [className] with [signature].
|
||||
//
|
||||
// This will try the host's foreign method binder first. If that fails, it
|
||||
// falls back to handling the built-in modules.
|
||||
// falls back to handling the built-in modules.instruction
|
||||
static WrenForeignMethodFn findForeignMethod(WrenVM* vm,
|
||||
const char* moduleName,
|
||||
const char* className,
|
||||
|
@ -421,7 +423,7 @@ static void runtimeError(WrenVM* vm)
|
|||
return;
|
||||
}
|
||||
|
||||
// Otherwise, unhook the caller since we will never resume and return to it.
|
||||
// Otherwise, unhook the caller since we will never re}sume and return to it.
|
||||
ObjFiber* caller = current->caller;
|
||||
current->caller = NULL;
|
||||
current = caller;
|
||||
|
@ -820,6 +822,10 @@ inline static bool checkArity(WrenVM* vm, Value value, int numArgs)
|
|||
return false;
|
||||
}
|
||||
|
||||
extern volatile int must_yield;
|
||||
extern jmp_buf sched_ctxbuf;
|
||||
// restore execution after a yield
|
||||
__attribute__((noreturn)) void wrenResume(WrenVM *vm) { longjmp(vm->ctxbuf, 1); }
|
||||
|
||||
// The main bytecode interpreter loop. This is where the magic happens. It is
|
||||
// also, as you can imagine, highly performance critical.
|
||||
|
@ -887,6 +893,16 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
|
|||
#define DEBUG_TRACE_INSTRUCTIONS() do { } while (false)
|
||||
#endif
|
||||
|
||||
#define CHECK_SCHEDULER() \
|
||||
do \
|
||||
{ \
|
||||
__builtin_expect(must_yield == 0, 1); \
|
||||
if (must_yield) { \
|
||||
if (!setjmp(vm->ctxbuf)) \
|
||||
longjmp(sched_ctxbuf, 1); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#if WREN_COMPUTED_GOTO
|
||||
|
||||
static void* dispatchTable[] = {
|
||||
|
@ -901,6 +917,7 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
|
|||
#define DISPATCH() \
|
||||
do \
|
||||
{ \
|
||||
CHECK_SCHEDULER(); \
|
||||
DEBUG_TRACE_INSTRUCTIONS(); \
|
||||
goto *dispatchTable[instruction = (Code)READ_BYTE()]; \
|
||||
} while (false)
|
||||
|
@ -909,6 +926,7 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
|
|||
|
||||
#define INTERPRET_LOOP \
|
||||
loop: \
|
||||
CHECK_SHEDULER(); \
|
||||
DEBUG_TRACE_INSTRUCTIONS(); \
|
||||
switch (instruction = (Code)READ_BYTE())
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#include "wren_value.h"
|
||||
#include "wren_utils.h"
|
||||
|
||||
#include <setjmp.h>
|
||||
|
||||
// The maximum number of temporary objects that can be made visible to the GC
|
||||
// at one time.
|
||||
#define WREN_MAX_TEMP_ROOTS 8
|
||||
|
@ -42,6 +44,9 @@ struct WrenVM
|
|||
ObjClass* rangeClass;
|
||||
ObjClass* stringClass;
|
||||
|
||||
// the interpreter saved context
|
||||
jmp_buf ctxbuf;
|
||||
|
||||
// The fiber that is currently running.
|
||||
ObjFiber* fiber;
|
||||
|
||||
|
|
Loading…
Reference in New Issue