mirror of
https://github.com/janet-lang/janet
synced 2025-01-10 23:50:26 +00:00
Change fiber signal model to add user signals. This
should allow easier implementations of eventloops, threadpools, or custom data flows with fibers.
This commit is contained in:
parent
0fd9224e4a
commit
51bdc41014
@ -87,7 +87,6 @@ static const DstInstructionDef dst_ops[] = {
|
||||
{"call", DOP_CALL},
|
||||
{"clo", DOP_CLOSURE},
|
||||
{"cmp", DOP_COMPARE},
|
||||
{"debug", DOP_DEBUG},
|
||||
{"div", DOP_DIVIDE},
|
||||
{"divi", DOP_DIVIDE_INTEGER},
|
||||
{"divim", DOP_DIVIDE_IMMEDIATE},
|
||||
@ -136,6 +135,7 @@ static const DstInstructionDef dst_ops[] = {
|
||||
{"ret", DOP_RETURN},
|
||||
{"retn", DOP_RETURN_NIL},
|
||||
{"setu", DOP_SET_UPVALUE},
|
||||
{"sig", DOP_SIGNAL},
|
||||
{"sl", DOP_SHIFT_LEFT},
|
||||
{"slim", DOP_SHIFT_LEFT_IMMEDIATE},
|
||||
{"sr", DOP_SHIFT_RIGHT},
|
||||
@ -144,8 +144,7 @@ static const DstInstructionDef dst_ops[] = {
|
||||
{"sruim", DOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE},
|
||||
{"sub", DOP_SUBTRACT},
|
||||
{"tcall", DOP_TAILCALL},
|
||||
{"tchck", DOP_TYPECHECK},
|
||||
{"yield", DOP_YIELD}
|
||||
{"tchck", DOP_TYPECHECK}
|
||||
};
|
||||
|
||||
/* Typename aliases for tchck instruction */
|
||||
|
@ -919,13 +919,14 @@ onvalue."
|
||||
(res)
|
||||
(do
|
||||
(:= good false)
|
||||
(onerr "compile" (get res :error))))) :dey))
|
||||
(onerr "compile" (get res :error))))) :a))
|
||||
(def res (fiber.resume f))
|
||||
(if good
|
||||
(cond
|
||||
(= (fiber.status f) :error) (onerr "runtime" res f)
|
||||
(= (fiber.status f) :debug) (onerr "debug" res f)
|
||||
going (onvalue res))))
|
||||
(when good
|
||||
(def sig (fiber.status f))
|
||||
(if going
|
||||
(if (= sig :dead)
|
||||
(onvalue res)
|
||||
(onerr "runtime" res f)))))
|
||||
|
||||
# Run loop
|
||||
(def oldenv *env*)
|
||||
|
@ -825,11 +825,12 @@ recur:
|
||||
dstc_cerror(c, ast, "macro expansion recursed too deeply");
|
||||
return dstc_cslot(dst_wrap_nil());
|
||||
} else {
|
||||
DstFiber *f = dst_fiber(dst_unwrap_function(fn), 64);
|
||||
DstFunction *f = dst_unwrap_function(fn);
|
||||
int lock = dst_gclock();
|
||||
x = dst_resume(f, dst_tuple_length(tup) - 1, tup + 1);
|
||||
DstSignal status = dst_call(f, dst_tuple_length(tup) - 1, tup + 1, &x);
|
||||
dst_gcunlock(lock);
|
||||
if (f->status == DST_FIBER_ERROR || f->status == DST_FIBER_DEBUG) {
|
||||
if (status != DST_SIGNAL_OK) {
|
||||
printf("Status: %d\n", status);
|
||||
const uint8_t *es = dst_formatc("error in macro expansion: %V", x);
|
||||
dstc_error(c, ast, es);
|
||||
}
|
||||
|
@ -43,8 +43,9 @@ int dst_dobytes(DstTable *env, const uint8_t *bytes, int32_t len) {
|
||||
if (cres.status == DST_COMPILE_OK) {
|
||||
DstFunction *f = dst_thunk(cres.funcdef);
|
||||
DstFiber *fiber = dst_fiber(f, 64);
|
||||
Dst ret = dst_run(fiber);
|
||||
if (fiber->status != DST_FIBER_DEAD) {
|
||||
Dst ret = dst_wrap_nil();
|
||||
DstSignal status = dst_run(fiber, &ret);
|
||||
if (status != DST_SIGNAL_OK) {
|
||||
printf("internal runtime error: %s\n", (const char *) dst_to_string(ret));
|
||||
errflags |= 0x01;
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ DstTable *dst_stl_env(int flags) {
|
||||
DOP_TAILCALL
|
||||
};
|
||||
static uint32_t debug_asm[] = {
|
||||
DOP_DEBUG,
|
||||
DOP_SIGNAL | (2 << 24),
|
||||
DOP_RETURN_NIL
|
||||
};
|
||||
DstTable *env = dst_table(0);
|
||||
@ -78,7 +78,7 @@ DstTable *dst_stl_env(int flags) {
|
||||
/* Load main functions */
|
||||
dst_env_cfuns(env, cfuns);
|
||||
|
||||
dst_env_def(env, "debug", dst_wrap_function(dst_quick_asm(0, 0, 0, debug_asm, sizeof(debug_asm))));
|
||||
dst_env_def(env, "debug", dst_wrap_function(dst_quick_asm(0, 0, 1, debug_asm, sizeof(debug_asm))));
|
||||
dst_env_def(env, "error", dst_wrap_function(dst_quick_asm(1, 0, 1, error_asm, sizeof(error_asm))));
|
||||
dst_env_def(env, "apply1", dst_wrap_function(dst_quick_asm(2, 0, 2, apply_asm, sizeof(apply_asm))));
|
||||
|
||||
|
@ -92,13 +92,12 @@ enum DstInstructionType dst_instructions[DOP_INSTRUCTION_COUNT] = {
|
||||
DIT_SS, /* DOP_CALL, */
|
||||
DIT_S, /* DOP_TAILCALL, */
|
||||
DIT_SSS, /* DOP_RESUME, */
|
||||
DIT_SS, /* DOP_YIELD, */
|
||||
DIT_SSU, /* DOP_SIGNAL, */
|
||||
DIT_SSS, /* DOP_GET, */
|
||||
DIT_SSS, /* DOP_PUT, */
|
||||
DIT_SSU, /* DOP_GET_INDEX, */
|
||||
DIT_SSU, /* DOP_PUT_INDEX, */
|
||||
DIT_SS, /* DOP_LENGTH */
|
||||
DIT_0 /* DOP_DEBUG */
|
||||
DIT_SS /* DOP_LENGTH */
|
||||
};
|
||||
|
||||
/* Verify some bytecode */
|
||||
|
@ -40,7 +40,6 @@ DstFiber *dst_fiber(DstFunction *callee, int32_t capacity) {
|
||||
fiber->data = data;
|
||||
}
|
||||
fiber->maxstack = DST_STACK_MAX;
|
||||
fiber->flags = DST_FIBER_MASK_DEBUG;
|
||||
return dst_fiber_reset(fiber, callee);
|
||||
}
|
||||
|
||||
@ -49,10 +48,10 @@ DstFiber *dst_fiber_reset(DstFiber *fiber, DstFunction *callee) {
|
||||
fiber->frame = 0;
|
||||
fiber->stackstart = DST_FRAME_SIZE;
|
||||
fiber->stacktop = DST_FRAME_SIZE;
|
||||
fiber->status = DST_FIBER_NEW;
|
||||
fiber->root = callee;
|
||||
fiber->child = NULL;
|
||||
fiber->flags |= DST_FIBER_FLAG_NEW;
|
||||
fiber->flags = DST_FIBER_MASK_YIELD;
|
||||
dst_fiber_set_status(fiber, DST_STATUS_NEW);
|
||||
return fiber;
|
||||
}
|
||||
|
||||
@ -269,22 +268,37 @@ static int cfun_new(DstArgs args) {
|
||||
const uint8_t *flags;
|
||||
int32_t len, i;
|
||||
DST_ARG_BYTES(flags, len, args, 1);
|
||||
fiber->flags |= DST_FIBER_MASK_ERROR | DST_FIBER_MASK_YIELD;
|
||||
fiber->flags = 0;
|
||||
dst_fiber_set_status(fiber, DST_STATUS_NEW);
|
||||
for (i = 0; i < len; i++) {
|
||||
switch (flags[i]) {
|
||||
default:
|
||||
DST_THROW(args, "invalid flag, expected d, e, or y");
|
||||
case ':':
|
||||
break;
|
||||
case 'd':
|
||||
fiber->flags &= ~DST_FIBER_MASK_DEBUG;
|
||||
break;
|
||||
case 'e':
|
||||
fiber->flags &= ~DST_FIBER_MASK_ERROR;
|
||||
break;
|
||||
case 'y':
|
||||
fiber->flags &= ~DST_FIBER_MASK_YIELD;
|
||||
break;
|
||||
if (flags[i] >= '0' && flags[i] <= '9') {
|
||||
fiber->flags |= DST_FIBER_MASK_USERN(flags[i] - '0');
|
||||
} else {
|
||||
switch (flags[i]) {
|
||||
default:
|
||||
DST_THROW(args, "invalid flag, expected a, d, e, u, or y");
|
||||
case ':':
|
||||
break;
|
||||
case 'a':
|
||||
fiber->flags |=
|
||||
DST_FIBER_MASK_DEBUG |
|
||||
DST_FIBER_MASK_ERROR |
|
||||
DST_FIBER_MASK_USER |
|
||||
DST_FIBER_MASK_YIELD;
|
||||
break;
|
||||
case 'd':
|
||||
fiber->flags |= DST_FIBER_MASK_DEBUG;
|
||||
break;
|
||||
case 'e':
|
||||
fiber->flags |= DST_FIBER_MASK_ERROR;
|
||||
break;
|
||||
case 'u':
|
||||
fiber->flags |= DST_FIBER_MASK_USER;
|
||||
break;
|
||||
case 'y':
|
||||
fiber->flags |= DST_FIBER_MASK_YIELD;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -296,25 +310,26 @@ static int cfun_status(DstArgs args) {
|
||||
const char *status = "";
|
||||
DST_FIXARITY(args, 1);
|
||||
DST_ARG_FIBER(fiber, args, 0);
|
||||
switch(fiber->status) {
|
||||
case DST_FIBER_PENDING:
|
||||
status = ":pending";
|
||||
break;
|
||||
case DST_FIBER_NEW:
|
||||
status = ":new";
|
||||
break;
|
||||
case DST_FIBER_ALIVE:
|
||||
status = ":alive";
|
||||
break;
|
||||
case DST_FIBER_DEAD:
|
||||
status = ":dead";
|
||||
break;
|
||||
case DST_FIBER_ERROR:
|
||||
status = ":error";
|
||||
break;
|
||||
case DST_FIBER_DEBUG:
|
||||
status = ":debug";
|
||||
break;
|
||||
uint32_t s = (fiber->flags & DST_FIBER_STATUS_MASK) >>
|
||||
DST_FIBER_STATUS_OFFSET;
|
||||
switch (s) {
|
||||
case DST_STATUS_DEAD: status = ":dead"; break;
|
||||
case DST_STATUS_ERROR: status = ":error"; break;
|
||||
case DST_STATUS_DEBUG: status = ":debug"; break;
|
||||
case DST_STATUS_PENDING: status = ":pending"; break;
|
||||
case DST_STATUS_USER0: status = ":user0"; break;
|
||||
case DST_STATUS_USER1: status = ":user1"; break;
|
||||
case DST_STATUS_USER2: status = ":user2"; break;
|
||||
case DST_STATUS_USER3: status = ":user3"; break;
|
||||
case DST_STATUS_USER4: status = ":user4"; break;
|
||||
case DST_STATUS_USER5: status = ":user5"; break;
|
||||
case DST_STATUS_USER6: status = ":user6"; break;
|
||||
case DST_STATUS_USER7: status = ":user7"; break;
|
||||
case DST_STATUS_USER8: status = ":user8"; break;
|
||||
case DST_STATUS_USER9: status = ":user9"; break;
|
||||
case DST_STATUS_NEW: status = ":new"; break;
|
||||
default:
|
||||
case DST_STATUS_ALIVE: status = ":alive"; break;
|
||||
}
|
||||
DST_RETURN_CSYMBOL(args, status);
|
||||
}
|
||||
@ -401,7 +416,7 @@ static const DstReg cfuns[] = {
|
||||
/* Module entry point */
|
||||
int dst_lib_fiber(DstArgs args) {
|
||||
static uint32_t yield_asm[] = {
|
||||
DOP_YIELD,
|
||||
DOP_SIGNAL | (3 << 24),
|
||||
DOP_RETURN
|
||||
};
|
||||
static uint32_t resume_asm[] = {
|
||||
|
@ -27,6 +27,11 @@
|
||||
|
||||
extern DST_THREAD_LOCAL DstFiber *dst_vm_fiber;
|
||||
|
||||
#define dst_fiber_set_status(f, s) do {\
|
||||
(f)->flags &= ~DST_FIBER_STATUS_MASK;\
|
||||
(f)->flags |= (s) << DST_FIBER_STATUS_OFFSET;\
|
||||
} while (0)
|
||||
|
||||
#define dst_stack_frame(s) ((DstStackFrame *)((s) - DST_FRAME_SIZE))
|
||||
#define dst_fiber_frame(f) dst_stack_frame((f)->data + (f)->frame)
|
||||
DstFiber *dst_fiber_reset(DstFiber *fiber, DstFunction *callee);
|
||||
|
@ -188,7 +188,8 @@ recur:
|
||||
return;
|
||||
dst_gc_mark(fiber);
|
||||
|
||||
if (fiber->flags & DST_FIBER_FLAG_NEW)
|
||||
/* Check if new fiber - all status bits sets indicate :new status */
|
||||
if ((fiber->flags & DST_FIBER_STATUS_MASK) == DST_FIBER_STATUS_MASK)
|
||||
dst_mark_function(fiber->root);
|
||||
|
||||
i = fiber->frame;
|
||||
|
223
src/core/vm.c
223
src/core/vm.c
@ -36,7 +36,7 @@ DST_THREAD_LOCAL DstFiber *dst_vm_fiber = NULL;
|
||||
if (dst_vm_next_collection >= dst_vm_gc_interval) dst_collect(); } while (0)
|
||||
|
||||
/* Start running the VM from where it left off. */
|
||||
Dst dst_run(DstFiber *fiber) {
|
||||
DstSignal dst_continue(DstFiber *fiber, Dst in, Dst *out) {
|
||||
|
||||
/* Save old fiber to reset */
|
||||
DstFiber *old_vm_fiber = dst_vm_fiber;
|
||||
@ -50,25 +50,56 @@ Dst dst_run(DstFiber *fiber) {
|
||||
* Values stored here should be used immediately */
|
||||
Dst retreg;
|
||||
|
||||
/* Signal to return when done */
|
||||
DstSignal signal = DST_SIGNAL_OK;
|
||||
|
||||
/* Ensure fiber is not alive, dead, or error */
|
||||
DstFiberStatus startstatus = dst_fiber_status(fiber);
|
||||
if (startstatus == DST_STATUS_ALIVE ||
|
||||
startstatus == DST_STATUS_DEAD ||
|
||||
startstatus == DST_STATUS_ERROR) {
|
||||
*out = dst_cstringv("cannot resume alive, dead, or errored fiber");
|
||||
return DST_SIGNAL_ERROR;
|
||||
}
|
||||
|
||||
/* Increment the stackn */
|
||||
if (dst_vm_stackn >= DST_RECURSION_GUARD) {
|
||||
fiber->status = DST_FIBER_ERROR;
|
||||
return dst_cstringv("C stack recursed too deeply");
|
||||
dst_fiber_set_status(fiber, DST_STATUS_ERROR);
|
||||
*out = dst_cstringv("C stack recursed too deeply");
|
||||
return DST_SIGNAL_ERROR;
|
||||
}
|
||||
dst_vm_stackn++;
|
||||
|
||||
/* Setup fiber state */
|
||||
dst_vm_fiber = fiber;
|
||||
dst_gcroot(dst_wrap_fiber(fiber));
|
||||
if (fiber->flags & DST_FIBER_FLAG_NEW) {
|
||||
dst_gcroot(in);
|
||||
if (startstatus == DST_STATUS_NEW) {
|
||||
dst_fiber_push(fiber, in);
|
||||
dst_fiber_funcframe(fiber, fiber->root);
|
||||
fiber->flags &= ~DST_FIBER_FLAG_NEW;
|
||||
}
|
||||
fiber->status = DST_FIBER_ALIVE;
|
||||
dst_fiber_set_status(fiber, DST_STATUS_ALIVE);
|
||||
stack = fiber->data + fiber->frame;
|
||||
pc = dst_stack_frame(stack)->pc;
|
||||
func = dst_stack_frame(stack)->func;
|
||||
|
||||
/* Used to extract bits from the opcode that correspond to arguments.
|
||||
* Pulls out unsigned integers */
|
||||
#define oparg(shift, mask) (((*pc) >> ((shift) << 3)) & (mask))
|
||||
|
||||
/* Check for child fiber. If there is a child, run child before self.
|
||||
* This should only be hit when the current fiber is pending on a RESUME
|
||||
* instruction. */
|
||||
if (fiber->child) {
|
||||
retreg = in;
|
||||
goto vm_resume_child;
|
||||
} else if (fiber->flags & DST_FIBER_FLAG_SIGNAL_WAITING) {
|
||||
/* If waiting for response to signal, use input and increment pc */
|
||||
stack[oparg(1, 0xFF)] = in;
|
||||
pc++;
|
||||
fiber->flags &= ~DST_FIBER_FLAG_SIGNAL_WAITING;
|
||||
}
|
||||
|
||||
/* Use computed gotos for GCC and clang, otherwise use switch */
|
||||
#ifdef __GNUC__
|
||||
#define VM_START() {vm_next();
|
||||
@ -143,13 +174,12 @@ static void *op_lookup[255] = {
|
||||
&&label_DOP_CALL,
|
||||
&&label_DOP_TAILCALL,
|
||||
&&label_DOP_RESUME,
|
||||
&&label_DOP_YIELD,
|
||||
&&label_DOP_SIGNAL,
|
||||
&&label_DOP_GET,
|
||||
&&label_DOP_PUT,
|
||||
&&label_DOP_GET_INDEX,
|
||||
&&label_DOP_PUT_INDEX,
|
||||
&&label_DOP_LENGTH,
|
||||
&&label_DOP_DEBUG,
|
||||
&&label_unknown_op
|
||||
};
|
||||
#else
|
||||
@ -162,10 +192,6 @@ static void *op_lookup[255] = {
|
||||
|
||||
#define vm_checkgc_next() dst_maybe_collect(); vm_next()
|
||||
|
||||
/* Used to extract bits from the opcode that correspond to arguments.
|
||||
* Pulls out unsigned integers */
|
||||
#define oparg(shift, mask) (((*pc) >> ((shift) << 3)) & (mask))
|
||||
|
||||
#define vm_throw(e) do { retreg = dst_cstringv(e); goto vm_error; } while (0)
|
||||
#define vm_assert(cond, e) do {if (!(cond)) vm_throw((e)); } while (0)
|
||||
|
||||
@ -212,8 +238,8 @@ static void *op_lookup[255] = {
|
||||
VM_START();
|
||||
|
||||
VM_DEFAULT();
|
||||
VM_OP(DOP_DEBUG)
|
||||
goto vm_debug;
|
||||
retreg = dst_wrap_nil();
|
||||
goto vm_exit;
|
||||
|
||||
VM_OP(DOP_NOOP)
|
||||
pc++;
|
||||
@ -565,9 +591,9 @@ static void *op_lookup[255] = {
|
||||
int32_t eindex = oparg(2, 0xFF);
|
||||
int32_t vindex = oparg(3, 0xFF);
|
||||
DstFuncEnv *env;
|
||||
vm_assert(func->def->environments_length > eindex, "invalid upvalue");
|
||||
vm_assert(func->def->environments_length > eindex, "invalid upvalue environment");
|
||||
env = func->envs[eindex];
|
||||
vm_assert(env->length > vindex, "invalid upvalue");
|
||||
vm_assert(env->length > vindex, "invalid upvalue index");
|
||||
if (env->offset) {
|
||||
/* On stack */
|
||||
stack[oparg(1, 0xFF)] = env->as.fiber->data[env->offset + vindex];
|
||||
@ -584,9 +610,9 @@ static void *op_lookup[255] = {
|
||||
int32_t eindex = oparg(2, 0xFF);
|
||||
int32_t vindex = oparg(3, 0xFF);
|
||||
DstFuncEnv *env;
|
||||
vm_assert(func->def->environments_length > eindex, "invalid upvalue");
|
||||
vm_assert(func->def->environments_length > eindex, "invalid upvalue environment");
|
||||
env = func->envs[eindex];
|
||||
vm_assert(env->length > vindex, "invalid upvalue");
|
||||
vm_assert(env->length > vindex, "invalid upvalue index");
|
||||
if (env->offset) {
|
||||
env->as.fiber->data[env->offset + vindex] = stack[oparg(1, 0xFF)];
|
||||
} else {
|
||||
@ -723,71 +749,21 @@ static void *op_lookup[255] = {
|
||||
|
||||
VM_OP(DOP_RESUME)
|
||||
{
|
||||
DstFiber *nextfiber;
|
||||
Dst fiberval = stack[oparg(2, 0xFF)];
|
||||
Dst val = stack[oparg(3, 0xFF)];
|
||||
vm_assert(dst_checktype(fiberval, DST_FIBER), "expected fiber");
|
||||
nextfiber = dst_unwrap_fiber(fiberval);
|
||||
switch (nextfiber->status) {
|
||||
default:
|
||||
vm_throw("expected pending, new, or debug fiber");
|
||||
case DST_FIBER_NEW:
|
||||
{
|
||||
dst_fiber_push(nextfiber, val);
|
||||
dst_fiber_funcframe(nextfiber, nextfiber->root);
|
||||
nextfiber->flags &= ~DST_FIBER_FLAG_NEW;
|
||||
break;
|
||||
}
|
||||
case DST_FIBER_DEBUG:
|
||||
{
|
||||
if (!nextfiber->child) {
|
||||
DstStackFrame *nextframe = dst_fiber_frame(nextfiber);
|
||||
nextframe->pc++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DST_FIBER_PENDING:
|
||||
{
|
||||
if (!nextfiber->child) {
|
||||
DstStackFrame *nextframe = dst_fiber_frame(nextfiber);
|
||||
nextfiber->data[nextfiber->frame + ((*nextframe->pc >> 8) & 0xFF)] = val;
|
||||
nextframe->pc++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
fiber->child = nextfiber;
|
||||
retreg = dst_run(nextfiber);
|
||||
dst_vm_fiber = fiber;
|
||||
switch (nextfiber->status) {
|
||||
case DST_FIBER_DEBUG:
|
||||
if (nextfiber->flags & DST_FIBER_MASK_DEBUG) goto vm_debug;
|
||||
fiber->child = NULL;
|
||||
break;
|
||||
case DST_FIBER_ERROR:
|
||||
if (nextfiber->flags & DST_FIBER_MASK_ERROR) goto vm_error;
|
||||
fiber->child = NULL;
|
||||
break;
|
||||
case DST_FIBER_PENDING:
|
||||
if (nextfiber->flags & DST_FIBER_MASK_YIELD) {
|
||||
fiber->status = DST_FIBER_PENDING;
|
||||
goto vm_exit;
|
||||
}
|
||||
fiber->child = NULL;
|
||||
break;
|
||||
default:
|
||||
fiber->child = NULL;
|
||||
break;
|
||||
}
|
||||
stack[oparg(1, 0xFF)] = retreg;
|
||||
pc++;
|
||||
vm_checkgc_next();
|
||||
retreg = stack[oparg(3, 0xFF)];
|
||||
fiber->child = dst_unwrap_fiber(fiberval);
|
||||
goto vm_resume_child;
|
||||
}
|
||||
|
||||
VM_OP(DOP_YIELD)
|
||||
VM_OP(DOP_SIGNAL)
|
||||
{
|
||||
retreg = stack[oparg(2, 0xFFFF)];
|
||||
fiber->status = DST_FIBER_PENDING;
|
||||
int32_t s = oparg(3, 0xFF);
|
||||
if (s > DST_SIGNAL_USER9) s = DST_SIGNAL_USER9;
|
||||
if (s < 0) s = 0;
|
||||
signal = s;
|
||||
retreg = stack[oparg(2, 0xFF)];
|
||||
fiber->flags |= DST_FIBER_FLAG_SIGNAL_WAITING;
|
||||
goto vm_exit;
|
||||
}
|
||||
|
||||
@ -828,7 +804,7 @@ static void *op_lookup[255] = {
|
||||
vm_return_cfunc:
|
||||
{
|
||||
dst_fiber_popframe(fiber);
|
||||
if (fiber->frame == 0) goto vm_return_root;
|
||||
if (fiber->frame == 0) goto vm_exit;
|
||||
stack = fiber->data + fiber->frame;
|
||||
stack[oparg(1, 0xFF)] = retreg;
|
||||
pc++;
|
||||
@ -840,7 +816,7 @@ static void *op_lookup[255] = {
|
||||
{
|
||||
dst_fiber_popframe(fiber);
|
||||
dst_fiber_popframe(fiber);
|
||||
if (fiber->frame == 0) goto vm_return_root;
|
||||
if (fiber->frame == 0) goto vm_exit;
|
||||
goto vm_reset;
|
||||
}
|
||||
|
||||
@ -848,40 +824,59 @@ static void *op_lookup[255] = {
|
||||
vm_return:
|
||||
{
|
||||
dst_fiber_popframe(fiber);
|
||||
if (fiber->frame == 0) goto vm_return_root;
|
||||
if (fiber->frame == 0) goto vm_exit;
|
||||
goto vm_reset;
|
||||
}
|
||||
|
||||
/* Exit loop with return value */
|
||||
vm_return_root:
|
||||
/* Resume a child fiber */
|
||||
vm_resume_child:
|
||||
{
|
||||
fiber->status = DST_FIBER_DEAD;
|
||||
goto vm_exit;
|
||||
DstFiber *child = fiber->child;
|
||||
DstFiberStatus status = dst_fiber_status(child);
|
||||
if (status == DST_STATUS_ALIVE ||
|
||||
status == DST_STATUS_DEAD ||
|
||||
status == DST_STATUS_ERROR) {
|
||||
vm_throw("cannot resume alive, dead, or errored fiber");
|
||||
}
|
||||
signal = dst_continue(child, retreg, &retreg);
|
||||
if (signal != DST_SIGNAL_OK) {
|
||||
if (child->flags & (1 << signal)) {
|
||||
/* Intercept signal */
|
||||
signal = DST_SIGNAL_OK;
|
||||
fiber->child = NULL;
|
||||
} else {
|
||||
/* Propogate signal */
|
||||
goto vm_exit;
|
||||
}
|
||||
}
|
||||
stack[oparg(1, 0xFF)] = retreg;
|
||||
pc++;
|
||||
vm_checkgc_next();
|
||||
}
|
||||
|
||||
/* Handle errors from c functions and vm opcodes */
|
||||
vm_error:
|
||||
{
|
||||
fiber->status = DST_FIBER_ERROR;
|
||||
signal = DST_SIGNAL_ERROR;
|
||||
goto vm_exit;
|
||||
}
|
||||
|
||||
/* Handle debugger interrupts */
|
||||
vm_debug:
|
||||
{
|
||||
fiber->status = DST_FIBER_DEBUG;
|
||||
retreg = dst_wrap_nil();
|
||||
goto vm_exit;
|
||||
}
|
||||
|
||||
/* Exit from vm loop */
|
||||
/* Exit from vm loop. If signal is not set explicitely, does
|
||||
* a successful return (DST_SIGNAL_OK). */
|
||||
vm_exit:
|
||||
{
|
||||
dst_stack_frame(stack)->pc = pc;
|
||||
dst_vm_stackn--;
|
||||
dst_gcunroot(in);
|
||||
dst_gcunroot(dst_wrap_fiber(fiber));
|
||||
dst_vm_fiber = old_vm_fiber;
|
||||
return retreg;
|
||||
*out = retreg;
|
||||
/* All statuses correspond to signals except new and alive,
|
||||
* which cannot be entered when exiting the vm loop.
|
||||
* DST_SIGNAL_OK -> DST_STATUS_DEAD
|
||||
* DST_SIGNAL_YIELD -> DST_STATUS_PENDING */
|
||||
dst_fiber_set_status(fiber, signal);
|
||||
return signal;
|
||||
}
|
||||
|
||||
/* Reset state of machine */
|
||||
@ -907,33 +902,17 @@ static void *op_lookup[255] = {
|
||||
#undef vm_binop_immediate
|
||||
|
||||
}
|
||||
|
||||
Dst dst_resume(DstFiber *fiber, int32_t argn, const Dst *argv) {
|
||||
switch (fiber->status) {
|
||||
default:
|
||||
dst_exit("expected new, pending or debug fiber");
|
||||
case DST_FIBER_DEBUG:
|
||||
break;
|
||||
case DST_FIBER_NEW:
|
||||
{
|
||||
int32_t i;
|
||||
for (i = 0; i < argn; i++)
|
||||
dst_fiber_push(fiber, argv[i]);
|
||||
dst_fiber_funcframe(fiber, fiber->root);
|
||||
fiber->flags &= ~DST_FIBER_FLAG_NEW;
|
||||
break;
|
||||
}
|
||||
case DST_FIBER_PENDING:
|
||||
{
|
||||
DstStackFrame *frame = dst_fiber_frame(fiber);
|
||||
fiber->data[fiber->frame + ((*frame->pc >> 8) & 0xFF)] = argn > 0
|
||||
? argv[0]
|
||||
: dst_wrap_nil();
|
||||
frame->pc++;
|
||||
break;
|
||||
}
|
||||
|
||||
DstSignal dst_call(DstFunction *fun, int32_t argn, const Dst *argv, Dst *out) {
|
||||
int32_t i;
|
||||
DstFiber *fiber = dst_fiber(fun, 64);
|
||||
for (i = 0; i < argn; i++) {
|
||||
dst_fiber_push(fiber, argv[i]);
|
||||
}
|
||||
return dst_run(fiber);
|
||||
dst_fiber_funcframe(fiber, fiber->root);
|
||||
/* Prevent push an extra value on the stack */
|
||||
dst_fiber_set_status(fiber, DST_STATUS_PENDING);
|
||||
return dst_continue(fiber, dst_wrap_nil(), out);
|
||||
}
|
||||
|
||||
/* Setup VM */
|
||||
|
@ -137,6 +137,7 @@ DstKV *dst_table_find(DstTable *t, Dst key);
|
||||
|
||||
/* Fiber */
|
||||
DstFiber *dst_fiber(DstFunction *callee, int32_t capacity);
|
||||
#define dst_fiber_status(f) (((f)->flags & DST_FIBER_STATUS_MASK) >> DST_FIBER_STATUS_OFFSET)
|
||||
|
||||
/* Treat similar types through uniform interfaces for iteration */
|
||||
int dst_seq_view(Dst seq, const Dst **data, int32_t *len);
|
||||
@ -167,7 +168,12 @@ void dst_gcunlock(int handle);
|
||||
DstFuncDef *dst_funcdef_alloc(void);
|
||||
DstFunction *dst_thunk(DstFuncDef *def);
|
||||
int dst_verify(DstFuncDef *def);
|
||||
DstFunction *dst_quick_asm(int32_t arity, int varargs, int32_t slots, const uint32_t *bytecode, size_t bytecode_size);
|
||||
DstFunction *dst_quick_asm(
|
||||
int32_t arity,
|
||||
int varargs,
|
||||
int32_t slots,
|
||||
const uint32_t *bytecode,
|
||||
size_t bytecode_size);
|
||||
|
||||
/* Misc */
|
||||
int dst_equals(Dst x, Dst y);
|
||||
@ -184,8 +190,9 @@ int dst_cstrcmp(const uint8_t *str, const char *other);
|
||||
/* VM functions */
|
||||
int dst_init(void);
|
||||
void dst_deinit(void);
|
||||
Dst dst_run(DstFiber *fiber);
|
||||
Dst dst_resume(DstFiber *fiber, int32_t argn, const Dst *argv);
|
||||
DstSignal dst_continue(DstFiber *fiber, Dst in, Dst *out);
|
||||
#define dst_run(F,O) dst_continue(F, dst_wrap_nil(), O)
|
||||
DstSignal dst_call(DstFunction *fun, int32_t argn, const Dst *argv, Dst *out);
|
||||
|
||||
/* Env helpers */
|
||||
void dst_env_def(DstTable *env, const char *name, Dst val);
|
||||
@ -205,9 +212,9 @@ int dst_typemany_err(DstArgs args, int32_t n, int expected);
|
||||
int dst_typeabstract_err(DstArgs args, int32_t n, const DstAbstractType *at);
|
||||
|
||||
/* Macros */
|
||||
#define DST_THROW(a, e) return (*((a).ret) = dst_cstringv(e), 1)
|
||||
#define DST_THROWV(a, v) return (*((a).ret) = (v), 1)
|
||||
#define DST_RETURN(a, v) return (*((a).ret) = (v), 0)
|
||||
#define DST_THROW(a, e) return (*((a).ret) = dst_cstringv(e), DST_SIGNAL_ERROR)
|
||||
#define DST_THROWV(a, v) return (*((a).ret) = (v), DST_SIGNAL_ERROR)
|
||||
#define DST_RETURN(a, v) return (*((a).ret) = (v), DST_SIGNAL_OK)
|
||||
|
||||
/* Early exit macros */
|
||||
#define DST_MAXARITY(A, N) do { if ((A).n > (N))\
|
||||
@ -286,7 +293,7 @@ int dst_typeabstract_err(DstArgs args, int32_t n, const DstAbstractType *at);
|
||||
#define DST_ARG_CFUNCTION(DEST, A, N) _DST_ARG(DST_CFUNCTION, cfunction, DEST, A, N)
|
||||
#define DST_ARG_ABSTRACT(DEST, A, N) _DST_ARG(DST_ABSTRACT, abstract, DEST, A, N)
|
||||
|
||||
#define DST_RETURN_NIL(A) return 0
|
||||
#define DST_RETURN_NIL(A) return DST_SIGNAL_OK
|
||||
#define DST_RETURN_FALSE(A) DST_RETURN(A, dst_wrap_false())
|
||||
#define DST_RETURN_TRUE(A) DST_RETURN(A, dst_wrap_true())
|
||||
#define DST_RETURN_BOOLEAN(A, X) DST_RETURN(A, dst_wrap_boolean(X))
|
||||
|
@ -124,13 +124,12 @@ enum DstOpCode {
|
||||
DOP_CALL,
|
||||
DOP_TAILCALL,
|
||||
DOP_RESUME,
|
||||
DOP_YIELD,
|
||||
DOP_SIGNAL,
|
||||
DOP_GET,
|
||||
DOP_PUT,
|
||||
DOP_GET_INDEX,
|
||||
DOP_PUT_INDEX,
|
||||
DOP_LENGTH,
|
||||
DOP_DEBUG,
|
||||
DOP_INSTRUCTION_COUNT
|
||||
};
|
||||
|
||||
|
@ -32,6 +32,44 @@ extern "C" {
|
||||
/* Names of all of the types */
|
||||
extern const char *const dst_type_names[16];
|
||||
|
||||
/* Fiber signals */
|
||||
typedef enum {
|
||||
DST_SIGNAL_OK,
|
||||
DST_SIGNAL_ERROR,
|
||||
DST_SIGNAL_DEBUG,
|
||||
DST_SIGNAL_YIELD,
|
||||
DST_SIGNAL_USER0,
|
||||
DST_SIGNAL_USER1,
|
||||
DST_SIGNAL_USER2,
|
||||
DST_SIGNAL_USER3,
|
||||
DST_SIGNAL_USER4,
|
||||
DST_SIGNAL_USER5,
|
||||
DST_SIGNAL_USER6,
|
||||
DST_SIGNAL_USER7,
|
||||
DST_SIGNAL_USER8,
|
||||
DST_SIGNAL_USER9
|
||||
} DstSignal;
|
||||
|
||||
/* Fiber statuses - mostly corresponds to signals. */
|
||||
typedef enum {
|
||||
DST_STATUS_DEAD,
|
||||
DST_STATUS_ERROR,
|
||||
DST_STATUS_DEBUG,
|
||||
DST_STATUS_PENDING,
|
||||
DST_STATUS_USER0,
|
||||
DST_STATUS_USER1,
|
||||
DST_STATUS_USER2,
|
||||
DST_STATUS_USER3,
|
||||
DST_STATUS_USER4,
|
||||
DST_STATUS_USER5,
|
||||
DST_STATUS_USER6,
|
||||
DST_STATUS_USER7,
|
||||
DST_STATUS_USER8,
|
||||
DST_STATUS_USER9,
|
||||
DST_STATUS_NEW,
|
||||
DST_STATUS_ALIVE
|
||||
} DstFiberStatus;
|
||||
|
||||
#ifdef DST_NANBOX
|
||||
typedef union Dst Dst;
|
||||
#else
|
||||
@ -339,18 +377,35 @@ struct DstArgs {
|
||||
};
|
||||
|
||||
/* Fiber flags */
|
||||
#define DST_FIBER_FLAG_NEW (1 << 31)
|
||||
#define DST_FIBER_FLAG_SIGNAL_WAITING (1 << 30)
|
||||
|
||||
/* Fiber signal masks. Should not overlap any fiber flags. */
|
||||
#define DST_FIBER_MASK_ERROR 1
|
||||
#define DST_FIBER_MASK_DEBUG 2
|
||||
#define DST_FIBER_MASK_YIELD 4
|
||||
/* Fiber signal masks. */
|
||||
#define DST_FIBER_MASK_ERROR 2
|
||||
#define DST_FIBER_MASK_DEBUG 4
|
||||
#define DST_FIBER_MASK_YIELD 8
|
||||
|
||||
#define DST_FIBER_MASK_USER0 (16 << 0)
|
||||
#define DST_FIBER_MASK_USER1 (16 << 1)
|
||||
#define DST_FIBER_MASK_USER2 (16 << 2)
|
||||
#define DST_FIBER_MASK_USER3 (16 << 3)
|
||||
#define DST_FIBER_MASK_USER4 (16 << 4)
|
||||
#define DST_FIBER_MASK_USER5 (16 << 5)
|
||||
#define DST_FIBER_MASK_USER6 (16 << 6)
|
||||
#define DST_FIBER_MASK_USER7 (16 << 7)
|
||||
#define DST_FIBER_MASK_USER8 (16 << 8)
|
||||
#define DST_FIBER_MASK_USER9 (16 << 9)
|
||||
|
||||
#define DST_FIBER_MASK_USERN(N) (16 << (N))
|
||||
#define DST_FIBER_MASK_USER 0x3FF0
|
||||
|
||||
#define DST_FIBER_STATUS_MASK 0xFF0000
|
||||
#define DST_FIBER_STATUS_OFFSET 16
|
||||
|
||||
/* A lightweight green thread in dst. Does not correspond to
|
||||
* operating system threads. */
|
||||
struct DstFiber {
|
||||
Dst *data;
|
||||
DstFiber *child; /* When a fiber enters the error or debug state, keep track of the original fiber that raised the error. */
|
||||
DstFiber *child; /* Keep linked list of fibers for restarting pending fibers */
|
||||
DstFunction *root; /* First value */
|
||||
int32_t frame; /* Index of the stack frame */
|
||||
int32_t stackstart; /* Beginning of next args */
|
||||
@ -358,14 +413,6 @@ struct DstFiber {
|
||||
int32_t capacity;
|
||||
int32_t maxstack; /* Arbitrary defined limit for stack overflow */
|
||||
uint32_t flags; /* Various flags */
|
||||
enum {
|
||||
DST_FIBER_PENDING,
|
||||
DST_FIBER_NEW,
|
||||
DST_FIBER_ALIVE,
|
||||
DST_FIBER_DEAD,
|
||||
DST_FIBER_ERROR,
|
||||
DST_FIBER_DEBUG
|
||||
} status;
|
||||
};
|
||||
|
||||
/* Mark if a stack frame is a tail call for debugging */
|
||||
|
@ -146,7 +146,7 @@
|
||||
# Fiber tests
|
||||
|
||||
(def afiber (fiber.new (fn [x]
|
||||
(error (string "hello, " x)))))
|
||||
(error (string "hello, " x))) :e))
|
||||
|
||||
(def afiber-result (fiber.resume afiber "world!"))
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user