Switch to assymetric coroutines instead of symmetric.

This commit is contained in:
Calvin Rose 2018-03-11 15:35:23 -04:00
parent 0b6ac1698c
commit 2a0dc5f1ad
19 changed files with 301 additions and 316 deletions

View File

@ -85,6 +85,7 @@ src/core/vm.c
src/core/wrap.c
src/core/gc.h
src/core/fiber.h
src/core/symcache.h
src/core/util.h
)

View File

@ -120,6 +120,7 @@ static const DstInstructionDef dst_ops[] = {
{"push3", DOP_PUSH_3},
{"put", DOP_PUT},
{"puti", DOP_PUT_INDEX},
{"res", DOP_RESUME},
{"ret", DOP_RETURN},
{"retn", DOP_RETURN_NIL},
{"setu", DOP_SET_UPVALUE},
@ -131,8 +132,8 @@ static const DstInstructionDef dst_ops[] = {
{"sruim", DOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE},
{"sub", DOP_SUBTRACT},
{"tcall", DOP_TAILCALL},
{"tran", DOP_TRANSFER},
{"tchck", DOP_TYPECHECK}
{"tchck", DOP_TYPECHECK},
{"yield", DOP_YIELD}
};
/* Check a dst string against a bunch of test_strings. Return the

View File

@ -25,9 +25,18 @@
(varset! i (+ i 1))
ret)
})
(defn fiber-seq [x]
{
:more (fn [] (or
(= (fiber-status x) :pending)
(= (fiber-status x) :new)))
:next (fn []
(resume x))
})
(def seqs {
:array array-seq
:tuple array-seq
:fiber fiber-seq
:struct (fn [x] x)})
(fn [x]
(def makeseq (get seqs (type x)))
@ -81,8 +90,7 @@
(tuple 'def endsym end)
(tuple 'while (tuple '< sym endsym)
(tuple-prepend body 'do)
(tuple 'varset! sym (tuple '+ sym 1))
)))
(tuple 'varset! sym (tuple '+ sym 1)))))
(defn pairs [x]
(var lastkey (next x nil))
@ -118,15 +126,15 @@
(var *read* nil)
(var *onvalue* identity)
(var *env* _env)
(var *env* (setproto @{} _env))
(def require-loading @{})
(defn onerr [t e]
(print (string t " error: " e)))
(defn char-stream [getchunk ondone]
(fiber (fn [parent]
(defn char-stream [getchunk]
(fiber (fn []
(def buf @"")
(var len 1)
(while (< 0 len)
@ -134,66 +142,75 @@
(getchunk buf)
(varset! len (length buf))
(for [i 0 len]
(transfer parent (get buf i))))
(ondone))))
(yield (get buf i))))
0)))
(defn val-stream [chars]
(fiber (fn [parent]
(var up parent)
(def me (fiber-current))
(defn val-stream [chars ondone]
(fiber (fn []
(def p (parser 1))
(while true
(var going true)
(while going
(def s (parser-status p))
(if (= s :full)
(varset! up (transfer up (parser-produce p)))
(yield (parser-produce p))
(if (= s :error)
(onerr "parse" (parser-error p))
(parser-byte p (transfer chars me))))))))
(do
(def stat (fiber-status chars))
(if (or (= :new stat) (= :pending stat))
(parser-byte p (resume chars))
(varset! going false))))))
(ondone))))
(defn require [path]
(when (get require-loading path)
(error (string "circular dependency: module " path "is already loading")))
(error (string "circular dependency: module " path " is already loading")))
(def oldread *read*)
(def oldonvalue *onvalue*)
(def oldenv *env*)
(def f (file-open path))
(def getter (fn [buf] (file-read f 1024 buf) buf))
(def cs (char-stream getter (fn []
(defn getter [buf] (file-read f 1024 buf) buf)
(defn resetter []
(put require-loading path nil)
(file-close f)
(varset! *read* oldread)
(varset! *onvalue* oldonvalue)
nil)))
(def vs (val-stream cs))
(varset! *env* oldenv)
(file-close f)
nil)
(def cs (char-stream getter))
(def vs (val-stream cs resetter))
(varset! *onvalue* identity)
(varset! *read* (fn [] (transfer vs (fiber-current))))
nil)
(varset! *read* (fn [] (resume vs)))
(varset! *env* (setproto @{} _env))
*env*)
(defn dorepl []
(def oldread *read*)
(def cs (char-stream (fn [buf]
(defn getter [buf]
(file-write stdout ">> ")
(file-read stdin :line buf))
(fn []
(defn resetter []
(varset! *read* oldread)
nil)))
(def vs (val-stream cs))
nil)
(def cs (char-stream getter))
(def vs (val-stream cs resetter))
(varset! *onvalue* (fn [ret]
(put _env '_ @{'value ret})
(put *env* '_ @{'value ret})
(describe ret)))
(varset! *read* (fn [] (transfer vs (fiber-current)))))
(varset! *read* (fn [] (resume vs))))
(defn dostring [str]
(def oldread *read*)
(def cs (char-stream (fn [buf]
(defn getter [buf]
(buffer-push-string buf str)
(buffer-push-string buf "\n")
buf)
(fn []
(buffer-push-string buf "\n"))
(defn resetter []
(varset! *read* oldread)
nil)))
(def vs (val-stream cs))
nil)
(def cs (char-stream getter))
(def vs (val-stream cs resetter))
(varset! *onvalue* identity)
(varset! *read* (fn [] (transfer vs (fiber-current)))))
(varset! *read* (fn [] (resume vs))))
(defn init-loop []
(while *read*
@ -204,7 +221,7 @@
(if (= (type res) :function)
(*onvalue* (res))
(onerr "compile" (get res :error)))))))
(def eb (transfer wrapper))
(if (= (fiber-status wrapper) :error) (onerr "runtime" eb))))
(def eb (resume wrapper))
(if (= (fiber-status wrapper) :error) (onerr "runtime" eb wrapper))))
(defn init-repl [] (dorepl) (init-loop))

View File

@ -813,7 +813,6 @@ recur:
} else {
/* Check macro */
DstTable *env = c->env;
int status;
Dst fn;
Dst entry = dst_table_get(env, headval);
for (;;) {
@ -825,8 +824,11 @@ recur:
dstc_cerror(c, ast, "macro expansion recursed too deeply");
return dstc_cslot(dst_wrap_nil());
} else {
status = dst_call_suspend(fn, &x, dst_tuple_length(tup) - 1, tup + 1);
if (status) {
DstFiber *f = dst_fiber(dst_unwrap_function(fn), 64);
int lock = dst_gclock();
x = dst_resume(f, dst_tuple_length(tup) - 1, tup + 1);
dst_gcunlock(lock);
if (f->status == DST_FIBER_ERROR || f->status == DST_FIBER_DEBUG) {
dstc_cerror(c, ast, "error in macro expansion");
}
/* Tail recur on the value */

View File

@ -41,8 +41,9 @@ int dst_dobytes(DstTable *env, const uint8_t *bytes, int32_t len) {
DstCompileResult cres = dst_compile(form, env, 0);
if (cres.status == DST_COMPILE_OK) {
DstFunction *f = dst_thunk(cres.funcdef);
Dst ret;
if (dst_run(dst_wrap_function(f), &ret)) {
DstFiber *fiber = dst_fiber(f, 64);
Dst ret = dst_run(fiber);
if (fiber->status != DST_FIBER_DEAD) {
printf("internal runtime error: %s\n", (const char *) dst_to_string(ret));
errflags |= 0x01;
}

View File

@ -43,7 +43,6 @@ static const DstReg cfuns[] = {
{"struct", dst_core_struct},
{"fiber", dst_core_fiber},
{"fiber-status", dst_core_fiber_status},
{"fiber-current", dst_core_fiber_current},
{"buffer", dst_core_buffer},
{"gensym", dst_core_gensym},
{"get", dst_core_get},
@ -74,13 +73,12 @@ DstTable *dst_stl_env() {
};
static uint32_t yield_asm[] = {
DOP_LOAD_NIL | (1 << 8),
DOP_TRANSFER | (1 << 16),
DOP_YIELD,
DOP_RETURN
};
static uint32_t transfer_asm[] = {
DOP_TRANSFER | (1 << 24),
static uint32_t resume_asm[] = {
DOP_RESUME | (1 << 24),
DOP_RETURN
};
@ -93,13 +91,10 @@ DstTable *dst_stl_env() {
dst_env_def(env, "error", dst_wrap_function(dst_quick_asm(1, 0, 1, error_asm, sizeof(error_asm))));
dst_env_def(env, "apply", dst_wrap_function(dst_quick_asm(2, 0, 2, apply_asm, sizeof(apply_asm))));
dst_env_def(env, "yield", dst_wrap_function(dst_quick_asm(1, 0, 2, yield_asm, sizeof(yield_asm))));
dst_env_def(env, "transfer", dst_wrap_function(dst_quick_asm(2, 0, 2, transfer_asm, sizeof(transfer_asm))));
dst_env_def(env, "resume", dst_wrap_function(dst_quick_asm(2, 0, 2, resume_asm, sizeof(resume_asm))));
dst_env_def(env, "VERSION", dst_cstringv(DST_VERSION));
/* Allow references to the environment */
dst_env_def(env, "_env", ret);
/* Set as gc root */
dst_gcroot(dst_wrap_table(env));
@ -119,6 +114,9 @@ DstTable *dst_stl_env() {
dst_lib_asm(args);
}
/* Allow references to the environment */
dst_env_def(env, "_env", ret);
/* Run bootstrap source */
dst_dobytes(env, dst_stl_bootstrap_gen, sizeof(dst_stl_bootstrap_gen));

View File

@ -80,7 +80,8 @@ enum DstInstructionType dst_instructions[DOP_INSTRUCTION_COUNT] = {
DIT_S, /* DOP_PUSH_ARRAY, */
DIT_SS, /* DOP_CALL, */
DIT_S, /* DOP_TAILCALL, */
DIT_SSS, /* DOP_TRANSFER, */
DIT_SSS, /* DOP_RESUME, */
DIT_SS, /* DOP_YIELD, */
DIT_SSS, /* DOP_GET, */
DIT_SSS, /* DOP_PUT, */
DIT_SSU, /* DOP_GET_INDEX, */

View File

@ -137,10 +137,9 @@ int dst_core_struct(DstArgs args) {
int dst_core_fiber(DstArgs args) {
DstFiber *fiber;
if (args.n < 1) return dst_throw(args, "expected at least one argument");
if (!dst_checktype(args.v[0], DST_FUNCTION)) return dst_throw(args, "expected a function");
fiber = dst_fiber(64);
fiber->parent = dst_vm_fiber;
dst_fiber_funcframe(fiber, dst_unwrap_function(args.v[0]));
if (!dst_checktype(args.v[0], DST_FUNCTION))
return dst_throw(args, "expected a function");
fiber = dst_fiber(dst_unwrap_function(args.v[0]), 64);
return dst_return(args, dst_wrap_fiber(fiber));
}
@ -226,10 +225,6 @@ int dst_core_fiber_status(DstArgs args) {
return dst_return(args, dst_csymbolv(status));
}
int dst_core_fiber_current(DstArgs args) {
return dst_return(args, dst_wrap_fiber(dst_vm_fiber));
}
int dst_core_put(DstArgs args) {
Dst ds, key, value;
DstArgs subargs = args;

View File

@ -21,11 +21,15 @@
*/
#include <dst/dst.h>
#include "fiber.h"
#include "gc.h"
/* Initialize a new fiber */
DstFiber *dst_fiber(int32_t capacity) {
DstFiber *dst_fiber(DstFunction *callee, int32_t capacity) {
DstFiber *fiber = dst_gcalloc(DST_MEMORY_FIBER, sizeof(DstFiber));
if (capacity < 16) {
capacity = 16;
}
fiber->capacity = capacity;
if (capacity) {
Dst *data = malloc(sizeof(Dst) * capacity);
@ -33,22 +37,21 @@ DstFiber *dst_fiber(int32_t capacity) {
DST_OUT_OF_MEMORY;
}
fiber->data = data;
} else {
fiber->data = NULL;
}
fiber->parent = NULL;
fiber->maxstack = DST_STACK_MAX;
fiber->flags = DST_FIBER_MASK_DEBUG;
return dst_fiber_reset(fiber);
return dst_fiber_reset(fiber, callee);
}
/* Clear a fiber (reset it) */
DstFiber *dst_fiber_reset(DstFiber *fiber) {
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->parent = NULL;
fiber->root = callee;
fiber->child = NULL;
fiber->flags |= DST_FIBER_FLAG_NEW;
return fiber;
}

41
src/core/fiber.h Normal file
View File

@ -0,0 +1,41 @@
/*
* Copyright (c) 2017 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef DST_FIBER_H_defined
#define DST_FIBER_H_defined
#include <dst/dst.h>
#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);
void dst_fiber_setcapacity(DstFiber *fiber, int32_t n);
void dst_fiber_push(DstFiber *fiber, Dst x);
void dst_fiber_push2(DstFiber *fiber, Dst x, Dst y);
void dst_fiber_push3(DstFiber *fiber, Dst x, Dst y, Dst z);
void dst_fiber_pushn(DstFiber *fiber, const Dst *arr, int32_t n);
void dst_fiber_funcframe(DstFiber *fiber, DstFunction *func);
void dst_fiber_funcframe_tail(DstFiber *fiber, DstFunction *func);
void dst_fiber_cframe(DstFiber *fiber);
void dst_fiber_popframe(DstFiber *fiber);
#endif

View File

@ -180,9 +180,13 @@ static void dst_mark_function(DstFunction *func) {
static void dst_mark_fiber(DstFiber *fiber) {
int32_t i, j;
DstStackFrame *frame;
recur:
if (dst_gc_reachable(fiber))
return;
dst_gc_mark(fiber);
if (fiber->flags & DST_FIBER_FLAG_NEW)
dst_mark_function(fiber->root);
i = fiber->frame;
j = fiber->stackstart - DST_FRAME_SIZE;
@ -198,8 +202,10 @@ static void dst_mark_fiber(DstFiber *fiber) {
i = frame->prevframe;
}
if (NULL != fiber->parent)
dst_mark_fiber(fiber->parent);
if (fiber->child) {
fiber = fiber->child;
goto recur;
}
}
/* Deinitialize a block of memory */
@ -309,8 +315,6 @@ void *dst_gcalloc(enum DstMemoryType type, size_t size) {
void dst_collect(void) {
uint32_t i;
if (dst_vm_gc_suspend) return;
if (dst_vm_fiber)
dst_mark_fiber(dst_vm_fiber);
for (i = 0; i < dst_vm_root_count; i++)
dst_mark(dst_vm_roots[i]);
dst_sweep();

View File

@ -249,8 +249,10 @@ static int dst_io_gc(void *p, size_t len) {
static int dst_io_fclose(DstArgs args) {
IOFile *iof = checkfile(args, 0);
if (!iof) return 1;
if (iof->flags & (IO_CLOSED | IO_NOT_CLOSEABLE))
return dst_throw(args, "could not close file");
if (iof->flags & (IO_CLOSED))
return dst_throw(args, "file already closed");
if (iof->flags & (IO_NOT_CLOSEABLE))
return dst_throw(args, "file not closable");
if (fclose(iof->file)) return dst_throw(args, "could not close file");
iof->flags |= IO_CLOSED;
return dst_return(args, dst_wrap_abstract(iof));

View File

@ -22,35 +22,15 @@
#include <dst/dst.h>
#include <dst/dstopcodes.h>
#include "symcache.h"
#include "fiber.h"
#include "gc.h"
#include "symcache.h"
/* VM State */
DstFiber *dst_vm_fiber = NULL;
/* VM state */
int dst_vm_stackn = 0;
/* Helper to ensure proper fiber is activated after returning */
static int dst_update_fiber(uint32_t mask) {
if (dst_vm_fiber->frame == 0) {
dst_vm_fiber->status = DST_FIBER_DEAD;
}
while (dst_vm_fiber->status == DST_FIBER_DEAD ||
dst_vm_fiber->status == DST_FIBER_ERROR ||
dst_vm_fiber->status == DST_FIBER_DEBUG ||
dst_vm_fiber->flags & mask) {
if (NULL != dst_vm_fiber->parent) {
dst_vm_fiber = dst_vm_fiber->parent;
} else {
/* The root thread has terminated */
return 1;
}
}
dst_vm_fiber->status = DST_FIBER_ALIVE;
return 0;
}
/* Start running the VM from where it left off. */
static int dst_continue(Dst *returnreg) {
Dst dst_run(DstFiber *fiber) {
/* VM state */
register Dst *stack;
@ -63,11 +43,22 @@ static int dst_continue(Dst *returnreg) {
/* Increment the stackn */
if (dst_vm_stackn >= DST_RECURSION_GUARD) {
*returnreg = dst_cstringv("C stack recursed too deeply");
return 1;
fiber->status = DST_FIBER_ERROR;
return dst_cstringv("C stack recursed too deeply");
}
dst_vm_stackn++;
/* Reset fiber state */
if (fiber->flags & DST_FIBER_FLAG_NEW) {
dst_fiber_funcframe(fiber, fiber->root);
fiber->flags &= ~DST_FIBER_FLAG_NEW;
}
fiber->status = DST_FIBER_ALIVE;
dst_gcroot(dst_wrap_fiber(fiber));
stack = fiber->data + fiber->frame;
pc = dst_stack_frame(stack)->pc;
func = dst_stack_frame(stack)->func;
/* Use computed gotos for GCC and clang, otherwise use switch */
#ifdef __GNUC__
#define VM_START() {vm_next();
@ -130,7 +121,8 @@ static void *op_lookup[255] = {
&&label_DOP_PUSH_ARRAY,
&&label_DOP_CALL,
&&label_DOP_TAILCALL,
&&label_DOP_TRANSFER,
&&label_DOP_RESUME,
&&label_DOP_YIELD,
&&label_DOP_GET,
&&label_DOP_PUT,
&&label_DOP_GET_INDEX,
@ -147,7 +139,7 @@ static void *op_lookup[255] = {
#define vm_next() continue
#endif
#define vm_checkgc_next() dst_maybe_collect(); vm_next()
#define vm_checkgc_next() do { dst_maybe_collect(); vm_next() } while (0)
/* Used to extract bits from the opcode that correspond to arguments.
* Pulls out unsigned integers */
@ -194,14 +186,6 @@ static void *op_lookup[255] = {
vm_next();\
}
#define vm_init_fiber_state() \
dst_vm_fiber->status = DST_FIBER_ALIVE;\
stack = dst_vm_fiber->data + dst_vm_fiber->frame;\
pc = dst_stack_frame(stack)->pc;\
func = dst_stack_frame(stack)->func;
vm_init_fiber_state();
/* Main interpreter loop. Sematically is a switch on
* (*pc & 0xFF) inside of an infinte loop. */
VM_START();
@ -513,8 +497,8 @@ static void *op_lookup[255] = {
if (!frame->env) {
/* Lazy capture of current stack frame */
DstFuncEnv *env = dst_gcalloc(DST_MEMORY_FUNCENV, sizeof(DstFuncEnv));
env->offset = dst_vm_fiber->frame;
env->as.fiber = dst_vm_fiber;
env->offset = fiber->frame;
env->as.fiber = fiber;
env->length = func->def->slotcount;
frame->env = env;
}
@ -527,29 +511,29 @@ static void *op_lookup[255] = {
stack[oparg(1, 0xFF)] = dst_wrap_function(fn);
pc++;
vm_checkgc_next();
}
}
VM_OP(DOP_PUSH)
dst_fiber_push(dst_vm_fiber, stack[oparg(1, 0xFFFFFF)]);
dst_fiber_push(fiber, stack[oparg(1, 0xFFFFFF)]);
pc++;
stack = dst_vm_fiber->data + dst_vm_fiber->frame;
stack = fiber->data + fiber->frame;
vm_checkgc_next();
VM_OP(DOP_PUSH_2)
dst_fiber_push2(dst_vm_fiber,
dst_fiber_push2(fiber,
stack[oparg(1, 0xFF)],
stack[oparg(2, 0xFFFF)]);
pc++;
stack = dst_vm_fiber->data + dst_vm_fiber->frame;
stack = fiber->data + fiber->frame;
vm_checkgc_next();
VM_OP(DOP_PUSH_3)
dst_fiber_push3(dst_vm_fiber,
dst_fiber_push3(fiber,
stack[oparg(1, 0xFF)],
stack[oparg(2, 0xFF)],
stack[oparg(3, 0xFF)]);
pc++;
stack = dst_vm_fiber->data + dst_vm_fiber->frame;
stack = fiber->data + fiber->frame;
vm_checkgc_next();
VM_OP(DOP_PUSH_ARRAY)
@ -557,35 +541,35 @@ static void *op_lookup[255] = {
const Dst *vals;
int32_t len;
if (dst_seq_view(stack[oparg(1, 0xFFFFFF)], &vals, &len)) {
dst_fiber_pushn(dst_vm_fiber, vals, len);
dst_fiber_pushn(fiber, vals, len);
} else {
vm_throw("expected array/tuple");
}
}
pc++;
stack = dst_vm_fiber->data + dst_vm_fiber->frame;
stack = fiber->data + fiber->frame;
vm_checkgc_next();
VM_OP(DOP_CALL)
{
Dst callee = stack[oparg(2, 0xFFFF)];
if (dst_vm_fiber->maxstack &&
dst_vm_fiber->stacktop > dst_vm_fiber->maxstack) {
if (fiber->maxstack &&
fiber->stacktop > fiber->maxstack) {
vm_throw("stack overflow");
}
if (dst_checktype(callee, DST_FUNCTION)) {
func = dst_unwrap_function(callee);
dst_stack_frame(stack)->pc = pc;
dst_fiber_funcframe(dst_vm_fiber, func);
stack = dst_vm_fiber->data + dst_vm_fiber->frame;
dst_fiber_funcframe(fiber, func);
stack = fiber->data + fiber->frame;
pc = func->def->bytecode;
vm_checkgc_next();
} else if (dst_checktype(callee, DST_CFUNCTION)) {
DstArgs args;
args.n = dst_vm_fiber->stacktop - dst_vm_fiber->stackstart;
dst_fiber_cframe(dst_vm_fiber);
args.n = fiber->stacktop - fiber->stackstart;
dst_fiber_cframe(fiber);
retreg = dst_wrap_nil();
args.v = dst_vm_fiber->data + dst_vm_fiber->frame;
args.v = fiber->data + fiber->frame;
args.ret = &retreg;
if (dst_unwrap_cfunction(callee)(args)) {
goto vm_error;
@ -600,16 +584,16 @@ static void *op_lookup[255] = {
Dst callee = stack[oparg(1, 0xFFFFFF)];
if (dst_checktype(callee, DST_FUNCTION)) {
func = dst_unwrap_function(callee);
dst_fiber_funcframe_tail(dst_vm_fiber, func);
stack = dst_vm_fiber->data + dst_vm_fiber->frame;
dst_fiber_funcframe_tail(fiber, func);
stack = fiber->data + fiber->frame;
pc = func->def->bytecode;
vm_checkgc_next();
} else if (dst_checktype(callee, DST_CFUNCTION)) {
DstArgs args;
args.n = dst_vm_fiber->stacktop - dst_vm_fiber->stackstart;
dst_fiber_cframe(dst_vm_fiber);
args.n = fiber->stacktop - fiber->stackstart;
dst_fiber_cframe(fiber);
retreg = dst_wrap_nil();
args.v = dst_vm_fiber->data + dst_vm_fiber->frame;
args.v = fiber->data + fiber->frame;
args.ret = &retreg;
if (dst_unwrap_cfunction(callee)(args)) {
goto vm_error;
@ -619,52 +603,59 @@ static void *op_lookup[255] = {
vm_throw("expected function");
}
VM_OP(DOP_TRANSFER)
VM_OP(DOP_RESUME)
{
int status;
DstFiber *nextfiber;
DstStackFrame *frame = dst_stack_frame(stack);
Dst temp = stack[oparg(2, 0xFF)];
retreg = stack[oparg(3, 0xFF)];
vm_assert(dst_checktype(temp, DST_FIBER) ||
dst_checktype(temp, DST_NIL), "expected fiber");
nextfiber = dst_checktype(temp, DST_FIBER)
? dst_unwrap_fiber(temp)
: dst_vm_fiber->parent;
/* Check for root fiber */
if (NULL == nextfiber) {
frame->pc = pc;
*returnreg = retreg;
dst_vm_stackn--;
return 0;
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 or new 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_PENDING:
{
DstStackFrame *nextframe = dst_fiber_frame(nextfiber);
nextfiber->data[nextfiber->frame + ((*nextframe->pc >> 8) & 0xFF)] = val;
nextframe->pc++;
break;
}
}
status = nextfiber->status;
vm_assert(status == DST_FIBER_PENDING ||
status == DST_FIBER_NEW, "can only transfer to new or pending fiber");
frame->pc = pc;
dst_vm_fiber->status = DST_FIBER_PENDING;
dst_vm_fiber = nextfiber;
vm_init_fiber_state();
if (status == DST_FIBER_PENDING) {
/* The next fiber is currently on a transfer instruction. */
stack[oparg(1, 0xFF)] = retreg;
pc++;
} else {
/* The next fiber is new and is on the first instruction */
if ((func->def->flags & DST_FUNCDEF_FLAG_VARARG) &&
!func->def->arity) {
/* Fully var arg function */
Dst *tup = dst_tuple_begin(1);
tup[0] = retreg;
stack[0] = dst_wrap_tuple(dst_tuple_end(tup));
} else if (func->def->arity) {
/* Non zero arity function */
stack[0] = retreg;
}
fiber->child = nextfiber;
retreg = dst_run(nextfiber);
switch (nextfiber->status) {
case DST_FIBER_DEBUG:
if (fiber->flags & DST_FIBER_MASK_DEBUG) goto vm_debug;
fiber->child = NULL;
break;
case DST_FIBER_ERROR:
if (fiber->flags & DST_FIBER_MASK_ERROR) goto vm_error;
fiber->child = NULL;
break;
default:
fiber->child = NULL;
if (fiber->flags & DST_FIBER_MASK_RETURN) goto vm_return_root;
break;
}
stack[oparg(1, 0xFF)] = retreg;
pc++;
vm_checkgc_next();
}
VM_OP(DOP_YIELD)
{
retreg = stack[oparg(2, 0xFFFF)];
fiber->status = DST_FIBER_PENDING;
goto vm_exit;
}
VM_OP(DOP_PUT)
dst_put(stack[oparg(1, 0xFF)],
stack[oparg(2, 0xFF)],
@ -698,12 +689,12 @@ static void *op_lookup[255] = {
++pc;
vm_next();
/* Return from c function. Simpler than retuning from dst function */
/* Return from c function. Simpler than returning from dst function */
vm_return_cfunc:
{
dst_fiber_popframe(dst_vm_fiber);
if (dst_update_fiber(DST_FIBER_MASK_RETURN)) goto vm_exit_value;
stack = dst_vm_fiber->data + dst_vm_fiber->frame;
dst_fiber_popframe(fiber);
if (fiber->frame == 0) goto vm_return_root;
stack = fiber->data + fiber->frame;
stack[oparg(1, 0xFF)] = retreg;
pc++;
vm_checkgc_next();
@ -712,39 +703,54 @@ static void *op_lookup[255] = {
/* Return from a cfunction that is in tail position (pop 2 stack frames) */
vm_return_cfunc_tail:
{
dst_fiber_popframe(dst_vm_fiber);
if (dst_update_fiber(DST_FIBER_MASK_RETURN)) goto vm_exit_value;
goto vm_return;
dst_fiber_popframe(fiber);
dst_fiber_popframe(fiber);
if (fiber->frame == 0) goto vm_return_root;
goto vm_reset;
}
/* Handle returning from stack frame. Expect return value in retreg */
vm_return:
{
dst_fiber_popframe(dst_vm_fiber);
if (dst_update_fiber(DST_FIBER_MASK_RETURN)) goto vm_exit_value;
dst_fiber_popframe(fiber);
if (fiber->frame == 0) goto vm_return_root;
goto vm_reset;
}
/* Exit loop with return value */
vm_return_root:
{
fiber->status = DST_FIBER_DEAD;
goto vm_exit;
}
/* Handle errors from c functions and vm opcodes */
vm_error:
{
dst_vm_fiber->status = DST_FIBER_ERROR;
if (dst_update_fiber(DST_FIBER_MASK_ERROR)) goto vm_exit_error;
goto vm_reset;
fiber->status = DST_FIBER_ERROR;
goto vm_exit;
}
/* Handle debugger interrupts */
vm_debug:
{
dst_vm_fiber->status = DST_FIBER_DEBUG;
if (dst_update_fiber(DST_FIBER_MASK_DEBUG)) goto vm_exit_debug;
goto vm_reset;
fiber->status = DST_FIBER_DEBUG;
goto vm_exit;
}
/* Exit from vm loop */
vm_exit:
{
dst_stack_frame(stack)->pc = pc;
dst_vm_stackn--;
dst_gcunroot(dst_wrap_fiber(fiber));
return retreg;
}
/* Reset state of machine */
vm_reset:
{
stack = dst_vm_fiber->data + dst_vm_fiber->frame;
stack = fiber->data + fiber->frame;
func = dst_stack_frame(stack)->func;
pc = dst_stack_frame(stack)->pc;
stack[oparg(1, 0xFF)] = retreg;
@ -752,30 +758,6 @@ static void *op_lookup[255] = {
vm_checkgc_next();
}
/* Exit loop with return value */
vm_exit_value:
{
*returnreg = retreg;
dst_vm_stackn--;
return 0;
}
/* Exit loop with error value */
vm_exit_error:
{
*returnreg = retreg;
dst_vm_stackn--;
return 1;
}
/* Exit loop with debug */
vm_exit_debug:
{
*returnreg = dst_wrap_nil();
dst_vm_stackn--;
return 2;
}
VM_END()
#undef oparg
@ -786,81 +768,33 @@ static void *op_lookup[255] = {
#undef vm_binop_real
#undef vm_binop_integer
#undef vm_binop_immediate
#undef vm_init_fiber_state
}
/* Run the vm with a given function. This function is
* called to start the vm. */
int dst_run(Dst callee, Dst *returnreg) {
if (dst_vm_fiber) {
dst_fiber_reset(dst_vm_fiber);
} else {
dst_vm_fiber = dst_fiber(64);
Dst dst_resume(DstFiber *fiber, int32_t argn, const Dst *argv) {
switch (fiber->status) {
default:
dst_exit("expected new or pending or fiber");
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;
}
}
if (dst_checktype(callee, DST_CFUNCTION)) {
DstArgs args;
*returnreg = dst_wrap_nil();
dst_fiber_cframe(dst_vm_fiber);
args.n = 0;
args.v = dst_vm_fiber->data + dst_vm_fiber->frame;
args.ret = returnreg;
return dst_unwrap_cfunction(callee)(args);
} else if (dst_checktype(callee, DST_FUNCTION)) {
dst_fiber_funcframe(dst_vm_fiber, dst_unwrap_function(callee));
return dst_continue(returnreg);
}
*returnreg = dst_cstringv("expected function");
return 1;
}
/* Helper for calling a function */
static int dst_call_help(Dst callee, Dst *returnreg, int32_t argn, const Dst* argv) {
dst_vm_fiber = dst_fiber(64);
dst_fiber_pushn(dst_vm_fiber, argv, argn);
if (dst_checktype(callee, DST_CFUNCTION)) {
DstArgs args;
*returnreg = dst_wrap_nil();
dst_fiber_cframe(dst_vm_fiber);
args.n = argn;
args.v = dst_vm_fiber->data + dst_vm_fiber->frame;
args.ret = returnreg;
return dst_unwrap_cfunction(callee)(args);
} else if (dst_checktype(callee, DST_FUNCTION)) {
dst_fiber_funcframe(dst_vm_fiber, dst_unwrap_function(callee));
return dst_continue(returnreg);
} else {
*returnreg = dst_cstringv("expected function");
return 1;
}
}
/* Run from inside a cfunction. This should only be used for
* short functions as it prevents re-entering the current fiber
* and suspend garbage collection. Currently used in the compiler
* for macro evaluation. */
int dst_call_suspend(Dst callee, Dst *returnreg, int32_t argn, const Dst *argv) {
int ret;
int lock;
DstFiber *oldfiber = dst_vm_fiber;
lock = dst_vm_gc_suspend++;
ret = dst_call_help(callee, returnreg, argn, argv);
dst_vm_fiber = oldfiber;
dst_vm_gc_suspend = lock;
return ret;
}
/* Run from inside a cfunction. This will not suspend GC, so
* the caller must be sure that no Dst*'s are left dangling in the calling function.
* Such values can be locked with dst_gcroot and unlocked with dst_gcunroot. */
int dst_call(Dst callee, Dst *returnreg, int32_t argn, const Dst *argv) {
int ret;
DstFiber *oldfiber = dst_vm_fiber;
dst_gcroot(dst_wrap_fiber(oldfiber));
ret = dst_call_help(callee, returnreg, argn, argv);
dst_gcunroot(dst_wrap_fiber(oldfiber));
dst_vm_fiber = oldfiber;
return ret;
return dst_run(fiber);
}
/* Setup functions */
@ -874,8 +808,6 @@ int dst_init() {
* there are no memory bugs during dev */
dst_vm_gc_interval = 0x100000;
dst_symcache_init();
/* Set thread */
dst_vm_fiber = NULL;
/* Initialize gc roots */
dst_vm_roots = NULL;
dst_vm_root_count = 0;
@ -886,7 +818,6 @@ int dst_init() {
/* Clear all memory associated with the VM */
void dst_deinit() {
dst_clear_memory();
dst_vm_fiber = NULL;
dst_symcache_deinit();
free(dst_vm_roots);
dst_vm_roots = NULL;

View File

@ -130,19 +130,7 @@ void dst_table_merge_struct(DstTable *table, const DstKV *other);
DstKV *dst_table_find(DstTable *t, Dst key);
/* Fiber */
DstFiber *dst_fiber(int32_t capacity);
#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);
void dst_fiber_setcapacity(DstFiber *fiber, int32_t n);
void dst_fiber_push(DstFiber *fiber, Dst x);
void dst_fiber_push2(DstFiber *fiber, Dst x, Dst y);
void dst_fiber_push3(DstFiber *fiber, Dst x, Dst y, Dst z);
void dst_fiber_pushn(DstFiber *fiber, const Dst *arr, int32_t n);
void dst_fiber_funcframe(DstFiber *fiber, DstFunction *func);
void dst_fiber_funcframe_tail(DstFiber *fiber, DstFunction *func);
void dst_fiber_cframe(DstFiber *fiber);
void dst_fiber_popframe(DstFiber *fiber);
DstFiber *dst_fiber(DstFunction *callee, int32_t capacity);
/* Treat similar types through uniform interfaces for iteration */
int dst_seq_view(Dst seq, const Dst **data, int32_t *len);
@ -169,7 +157,7 @@ int dst_gcunrootall(Dst root);
#define dst_maybe_collect() do {\
if (dst_vm_next_collection >= dst_vm_gc_interval) dst_collect(); } while (0)
#define dst_gclock() (dst_vm_gc_suspend++)
#define dst_gcunlock() (dst_vm_gc_suspend--)
#define dst_gcunlock(lock) (dst_vm_gc_suspend = lock)
/* Functions */
DstFuncDef *dst_funcdef_alloc(void);
@ -192,9 +180,8 @@ int dst_cstrcmp(const uint8_t *str, const char *other);
/* VM functions */
int dst_init(void);
void dst_deinit(void);
int dst_run(Dst callee, Dst *returnreg);
int dst_call(Dst callee, Dst *returnreg, int32_t argn, const Dst *argv);
int dst_call_suspend(Dst callee, Dst *returnreg, int32_t argn, const Dst *argv);
Dst dst_run(DstFiber *fiber);
Dst dst_resume(DstFiber *fiber, int32_t argn, const Dst *argv);
/* C Function helpers */
#define dst_throw(a, e) (*((a).ret) = dst_cstringv(e), 1)

View File

@ -94,7 +94,7 @@ int dst_core_rawget(DstArgs args);
int dst_core_getproto(DstArgs args);
int dst_core_setproto(DstArgs args);
int dst_core_fiber_status(DstArgs args);
int dst_core_fiber_current(DstArgs args);
int dst_core_fiber_location(DstArgs args);
int dst_core_put(DstArgs args);
int dst_core_gccollect(DstArgs args);
int dst_core_gcsetinterval(DstArgs args);

View File

@ -112,7 +112,8 @@ enum DstOpCode {
DOP_PUSH_ARRAY,
DOP_CALL,
DOP_TAILCALL,
DOP_TRANSFER,
DOP_RESUME,
DOP_YIELD,
DOP_GET,
DOP_PUT,
DOP_GET_INDEX,

View File

@ -57,9 +57,6 @@ extern Dst *dst_vm_roots;
extern uint32_t dst_vm_root_count;
extern uint32_t dst_vm_root_capacity;
/* GC roots - TODO consider a top level fiber pool (per thread?) */
extern DstFiber *dst_vm_fiber;
#ifdef __cplusplus
}
#endif

View File

@ -310,11 +310,14 @@ struct DstArgs {
#define DST_FIBER_MASK_ERROR 2
#define DST_FIBER_MASK_DEBUG 4
#define DST_FIBER_FLAG_NEW 8
/* A lightweight green thread in dst. Does not correspond to
* operating system threads. */
struct DstFiber {
Dst *data;
DstFiber *parent;
DstFiber *child; /* When a fiber enters the error or debug state, keep track of the original fiber that raised the error. */
DstFunction *root; /* First value */
int32_t frame; /* Index of the stack frame */
int32_t stackstart; /* Beginning of next args */
int32_t stacktop; /* Top of stack. Where values are pushed and popped from. */

View File

@ -160,18 +160,18 @@
(def afiber (fiber (fn [x]
(error (string "hello, " x)))))
(def afiber-result (transfer afiber "world!"))
(def afiber-result (resume afiber "world!"))
(assert (= afiber-result "hello, world!") "fiber error result")
(assert (= (fiber-status afiber) :error) "fiber error status")
# yield tests
(def t (fiber (fn [] (transfer nil 1) (yield 2) 3)))
(def t (fiber (fn [] (yield 1) (yield 2) 3)))
(assert (= 1 (transfer t)) "initial transfer to new fiber")
(assert (= 2 (transfer t)) "second transfer to fiber")
(assert (= 3 (transfer t)) "return from fiber")
(assert (= 1 (resume t)) "initial transfer to new fiber")
(assert (= 2 (resume t)) "second transfer to fiber")
(assert (= 3 (resume t)) "return from fiber")
(assert (= (fiber-status t) :dead) "finished fiber is dead")
# Var arg tests