1
0
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:
Calvin Rose 2018-05-16 22:09:36 -04:00
parent 0fd9224e4a
commit 51bdc41014
14 changed files with 258 additions and 204 deletions

View File

@ -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 */

View File

@ -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*)

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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))));

View File

@ -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 */

View File

@ -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[] = {

View File

@ -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);

View File

@ -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;

View File

@ -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 */

View File

@ -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))

View File

@ -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
};

View File

@ -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 */

View File

@ -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!"))