1
0
mirror of https://github.com/janet-lang/janet synced 2024-11-28 11:09:54 +00:00

Add ability to Janet signal from C functions.

While C functions are not re-entrant, signaling from a C function
can be used to implement async returns. When resuming a fiber that
signalled from within a C function, the fiber is started after the
instruction that emitted the signal. The resume argument is used
as the return result from the c function.
This commit is contained in:
Calvin Rose 2020-01-10 17:25:10 -06:00
parent ed5027db5d
commit 3d40c95e80
8 changed files with 90 additions and 60 deletions

View File

@ -27,20 +27,25 @@
#include "fiber.h" #include "fiber.h"
#endif #endif
void janet_panicv(Janet message) { void janet_signalv(JanetSignal sig, Janet message) {
if (janet_vm_return_reg != NULL) { if (janet_vm_return_reg != NULL) {
*janet_vm_return_reg = message; *janet_vm_return_reg = message;
janet_vm_fiber->flags |= JANET_FIBER_DID_LONGJUMP;
#if defined(JANET_BSD) || defined(JANET_APPLE) #if defined(JANET_BSD) || defined(JANET_APPLE)
_longjmp(*janet_vm_jmp_buf, 1); _longjmp(*janet_vm_jmp_buf, sig);
#else #else
longjmp(*janet_vm_jmp_buf, 1); longjmp(*janet_vm_jmp_buf, sig);
#endif #endif
} else { } else {
fputs((const char *)janet_formatc("janet top level panic - %v\n", message), stdout); fputs((const char *)janet_formatc("janet top level signal - %v\n", message), stdout);
exit(1); exit(1);
} }
} }
void janet_panicv(Janet message) {
janet_signalv(JANET_SIGNAL_ERROR, message);
}
void janet_panicf(const char *format, ...) { void janet_panicf(const char *format, ...) {
va_list args; va_list args;
const uint8_t *ret; const uint8_t *ret;

View File

@ -35,7 +35,7 @@ static void fiber_reset(JanetFiber *fiber) {
fiber->stackstart = JANET_FRAME_SIZE; fiber->stackstart = JANET_FRAME_SIZE;
fiber->stacktop = JANET_FRAME_SIZE; fiber->stacktop = JANET_FRAME_SIZE;
fiber->child = NULL; fiber->child = NULL;
fiber->flags = JANET_FIBER_MASK_YIELD; fiber->flags = JANET_FIBER_MASK_YIELD | JANET_FIBER_RESUME_NO_USEVAL | JANET_FIBER_RESUME_NO_SKIP;
fiber->env = NULL; fiber->env = NULL;
janet_fiber_set_status(fiber, JANET_STATUS_NEW); janet_fiber_set_status(fiber, JANET_STATUS_NEW);
} }
@ -369,7 +369,7 @@ static Janet cfun_fiber_new(int32_t argc, Janet *argv) {
if (argc == 2) { if (argc == 2) {
int32_t i; int32_t i;
JanetByteView view = janet_getbytes(argv, 1); JanetByteView view = janet_getbytes(argv, 1);
fiber->flags = 0; fiber->flags = JANET_FIBER_RESUME_NO_USEVAL | JANET_FIBER_RESUME_NO_SKIP;
janet_fiber_set_status(fiber, JANET_STATUS_NEW); janet_fiber_set_status(fiber, JANET_STATUS_NEW);
for (i = 0; i < view.len; i++) { for (i = 0; i < view.len; i++) {
if (view.bytes[i] >= '0' && view.bytes[i] <= '9') { if (view.bytes[i] >= '0' && view.bytes[i] <= '9') {

View File

@ -27,6 +27,34 @@
#include <janet.h> #include <janet.h>
#endif #endif
/* Fiber signal masks. */
#define JANET_FIBER_MASK_ERROR 2
#define JANET_FIBER_MASK_DEBUG 4
#define JANET_FIBER_MASK_YIELD 8
#define JANET_FIBER_MASK_USER0 (16 << 0)
#define JANET_FIBER_MASK_USER1 (16 << 1)
#define JANET_FIBER_MASK_USER2 (16 << 2)
#define JANET_FIBER_MASK_USER3 (16 << 3)
#define JANET_FIBER_MASK_USER4 (16 << 4)
#define JANET_FIBER_MASK_USER5 (16 << 5)
#define JANET_FIBER_MASK_USER6 (16 << 6)
#define JANET_FIBER_MASK_USER7 (16 << 7)
#define JANET_FIBER_MASK_USER8 (16 << 8)
#define JANET_FIBER_MASK_USER9 (16 << 9)
#define JANET_FIBER_MASK_USERN(N) (16 << (N))
#define JANET_FIBER_MASK_USER 0x3FF0
#define JANET_FIBER_STATUS_MASK 0xFF0000
#define JANET_FIBER_STATUS_OFFSET 16
#define JANET_FIBER_BREAKPOINT 0x1000000
#define JANET_FIBER_RESUME_NO_USEVAL 0x2000000
#define JANET_FIBER_RESUME_NO_SKIP 0x4000000
#define JANET_FIBER_DID_LONGJUMP 0x8000000
#define JANET_FIBER_FLAG_MASK 0xF000000
extern JANET_THREAD_LOCAL JanetFiber *janet_vm_fiber; extern JANET_THREAD_LOCAL JanetFiber *janet_vm_fiber;
#define janet_fiber_set_status(f, s) do {\ #define janet_fiber_set_status(f, s) do {\

View File

@ -161,13 +161,16 @@ static Janet os_arch(int32_t argc, Janet *argv) {
static Janet os_exit(int32_t argc, Janet *argv) { static Janet os_exit(int32_t argc, Janet *argv) {
janet_arity(argc, 0, 1); janet_arity(argc, 0, 1);
int status;
if (argc == 0) { if (argc == 0) {
exit(EXIT_SUCCESS); status = EXIT_SUCCESS;
} else if (janet_checkint(argv[0])) { } else if (janet_checkint(argv[0])) {
exit(janet_unwrap_integer(argv[0])); status = janet_unwrap_integer(argv[0]);
} else { } else {
exit(EXIT_FAILURE); status = EXIT_FAILURE;
} }
janet_deinit();
exit(status);
return janet_wrap_nil(); return janet_wrap_nil();
} }

View File

@ -268,7 +268,7 @@ void janet_cfuns(JanetTable *env, const char *regprefix, const JanetReg *cfuns)
size_t bufsize = 0; size_t bufsize = 0;
if (NULL != regprefix) { if (NULL != regprefix) {
prefixlen = strlen(regprefix); prefixlen = strlen(regprefix);
bufsize = (prefixlen + 1) * 3; bufsize = prefixlen + 256;
longname_buffer = malloc(bufsize); longname_buffer = malloc(bufsize);
if (NULL == longname_buffer) { if (NULL == longname_buffer) {
JANET_OUT_OF_MEMORY; JANET_OUT_OF_MEMORY;

View File

@ -232,7 +232,7 @@ static Janet resolve_method(Janet name, JanetFiber *fiber) {
} }
/* Interpreter main loop */ /* Interpreter main loop */
static JanetSignal run_vm(JanetFiber *fiber, Janet in, JanetFiberStatus status) { static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
/* opcode -> label lookup if using clang/GCC */ /* opcode -> label lookup if using clang/GCC */
#ifdef JANET_USE_COMPUTED_GOTOS #ifdef JANET_USE_COMPUTED_GOTOS
@ -501,29 +501,38 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in, JanetFiberStatus status)
register JanetFunction *func; register JanetFunction *func;
vm_restore(); vm_restore();
/* Only should be hit if the fiber is either waiting for a child, or if (fiber->flags & JANET_FIBER_DID_LONGJUMP) {
* waiting to be resumed. In those cases, use input and increment pc. We if (janet_fiber_frame(fiber)->func == NULL) {
* DO NOT use input when resuming a fiber that has been interrupted at a /* Inside a c function */
* breakpoint. */ janet_fiber_popframe(fiber);
uint8_t first_opcode; vm_restore();
if (status != JANET_STATUS_NEW &&
((*pc & 0xFF) == JOP_SIGNAL ||
(*pc & 0xFF) == JOP_PROPAGATE ||
(*pc & 0xFF) == JOP_RESUME)) {
stack[A] = in;
pc++;
first_opcode = *pc & 0xFF;
} else if (status == JANET_STATUS_DEBUG) {
first_opcode = *pc & 0x7F;
} else {
first_opcode = *pc & 0xFF;
} }
/* Check if we were at a tail call instruction. If so, do implicit return */
if ((*pc & 0xFF) == JOP_TAILCALL) {
/* Tail call resume */
int entrance_frame = janet_stack_frame(stack)->flags & JANET_STACKFRAME_ENTRANCE;
janet_fiber_popframe(fiber);
if (entrance_frame) {
fiber->flags &= ~JANET_FIBER_FLAG_MASK;
vm_return(JANET_SIGNAL_OK, in);
}
vm_restore();
}
}
if (!(fiber->flags & JANET_FIBER_RESUME_NO_USEVAL)) stack[A] = in;
if (!(fiber->flags & JANET_FIBER_RESUME_NO_SKIP)) pc++;
uint8_t first_opcode = *pc & ((fiber->flags & JANET_FIBER_BREAKPOINT) ? 0x7F : 0xFF);
fiber->flags &= ~JANET_FIBER_FLAG_MASK;
/* Main interpreter loop. Semantically is a switch on /* Main interpreter loop. Semantically is a switch on
* (*pc & 0xFF) inside of an infinite loop. */ * (*pc & 0xFF) inside of an infinite loop. */
VM_START(); VM_START();
VM_DEFAULT(); VM_DEFAULT();
fiber->flags |= JANET_FIBER_BREAKPOINT | JANET_FIBER_RESUME_NO_USEVAL | JANET_FIBER_RESUME_NO_SKIP;
vm_return(JANET_SIGNAL_DEBUG, janet_wrap_nil()); vm_return(JANET_SIGNAL_DEBUG, janet_wrap_nil());
VM_OP(JOP_NOOP) VM_OP(JOP_NOOP)
@ -876,8 +885,9 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in, JanetFiberStatus status)
JanetFiber *child = janet_unwrap_fiber(stack[B]); JanetFiber *child = janet_unwrap_fiber(stack[B]);
fiber->child = child; fiber->child = child;
JanetSignal sig = janet_continue(child, stack[C], &retreg); JanetSignal sig = janet_continue(child, stack[C], &retreg);
if (sig != JANET_SIGNAL_OK && !(child->flags & (1 << sig))) if (sig != JANET_SIGNAL_OK && !(child->flags & (1 << sig))) {
vm_return(sig, retreg); vm_return(sig, retreg);
}
fiber->child = NULL; fiber->child = NULL;
stack = fiber->data + fiber->frame; stack = fiber->data + fiber->frame;
stack[A] = retreg; stack[A] = retreg;
@ -901,18 +911,22 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in, JanetFiberStatus status)
janet_panicf("cannot propagate from fiber with status :%s", janet_panicf("cannot propagate from fiber with status :%s",
janet_status_names[sub_status]); janet_status_names[sub_status]);
} }
janet_vm_fiber->child = f; fiber->child = f;
vm_return((int) sub_status, stack[B]); vm_return((int) sub_status, stack[B]);
} }
VM_OP(JOP_PUT) VM_OP(JOP_PUT)
vm_commit(); vm_commit();
fiber->flags |= JANET_FIBER_RESUME_NO_USEVAL;
janet_put(stack[A], stack[B], stack[C]); janet_put(stack[A], stack[B], stack[C]);
fiber->flags &= ~JANET_FIBER_RESUME_NO_USEVAL;
vm_checkgc_pcnext(); vm_checkgc_pcnext();
VM_OP(JOP_PUT_INDEX) VM_OP(JOP_PUT_INDEX)
vm_commit(); vm_commit();
fiber->flags |= JANET_FIBER_RESUME_NO_USEVAL;
janet_putindex(stack[A], C, stack[B]); janet_putindex(stack[A], C, stack[B]);
fiber->flags &= ~JANET_FIBER_RESUME_NO_USEVAL;
vm_checkgc_pcnext(); vm_checkgc_pcnext();
VM_OP(JOP_IN) VM_OP(JOP_IN)
@ -1094,9 +1108,8 @@ Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv) {
int handle = janet_gclock(); int handle = janet_gclock();
/* Run vm */ /* Run vm */
JanetSignal signal = run_vm(janet_vm_fiber, janet_vm_fiber->flags |= JANET_FIBER_RESUME_NO_USEVAL | JANET_FIBER_RESUME_NO_SKIP;
janet_wrap_nil(), JanetSignal signal = run_vm(janet_vm_fiber, janet_wrap_nil());
JANET_STATUS_ALIVE);
/* Teardown */ /* Teardown */
janet_vm_stackn = oldn; janet_vm_stackn = oldn;
@ -1156,14 +1169,16 @@ JanetSignal janet_continue(JanetFiber *fiber, Janet in, Janet *out) {
/* Run loop */ /* Run loop */
JanetSignal signal; JanetSignal signal;
int jmpsig;
#if defined(JANET_BSD) || defined(JANET_APPLE) #if defined(JANET_BSD) || defined(JANET_APPLE)
if (_setjmp(buf)) { jmpsig = _setjmp(buf);
#else #else
if (setjmp(buf)) { jmpsig = setjmp(buf);
#endif #endif
signal = JANET_SIGNAL_ERROR; if (jmpsig) {
signal = (JanetSignal) jmpsig;
} else { } else {
signal = run_vm(fiber, in, old_status); signal = run_vm(fiber, in);
} }
/* Tear down fiber */ /* Tear down fiber */

View File

@ -696,28 +696,6 @@ struct JanetGCObject {
JanetGCObject *next; JanetGCObject *next;
}; };
/* Fiber signal masks. */
#define JANET_FIBER_MASK_ERROR 2
#define JANET_FIBER_MASK_DEBUG 4
#define JANET_FIBER_MASK_YIELD 8
#define JANET_FIBER_MASK_USER0 (16 << 0)
#define JANET_FIBER_MASK_USER1 (16 << 1)
#define JANET_FIBER_MASK_USER2 (16 << 2)
#define JANET_FIBER_MASK_USER3 (16 << 3)
#define JANET_FIBER_MASK_USER4 (16 << 4)
#define JANET_FIBER_MASK_USER5 (16 << 5)
#define JANET_FIBER_MASK_USER6 (16 << 6)
#define JANET_FIBER_MASK_USER7 (16 << 7)
#define JANET_FIBER_MASK_USER8 (16 << 8)
#define JANET_FIBER_MASK_USER9 (16 << 9)
#define JANET_FIBER_MASK_USERN(N) (16 << (N))
#define JANET_FIBER_MASK_USER 0x3FF0
#define JANET_FIBER_STATUS_MASK 0xFF0000
#define JANET_FIBER_STATUS_OFFSET 16
/* A lightweight green thread in janet. Does not correspond to /* A lightweight green thread in janet. Does not correspond to
* operating system threads. */ * operating system threads. */
struct JanetFiber { struct JanetFiber {
@ -1388,6 +1366,7 @@ JANET_API Janet janet_resolve_core(const char *name);
} \ } \
JANET_API void JANET_ENTRY_NAME JANET_API void JANET_ENTRY_NAME
JANET_NO_RETURN JANET_API void janet_signalv(JanetSignal signal, Janet message);
JANET_NO_RETURN JANET_API void janet_panicv(Janet message); JANET_NO_RETURN JANET_API void janet_panicv(Janet message);
JANET_NO_RETURN JANET_API void janet_panic(const char *message); JANET_NO_RETURN JANET_API void janet_panic(const char *message);
JANET_NO_RETURN JANET_API void janet_panics(JanetString message); JANET_NO_RETURN JANET_API void janet_panics(JanetString message);

View File

@ -203,12 +203,12 @@
(defn check-match (defn check-match
[pat text should-match] [pat text should-match]
(def result (peg/match pat text)) (def result (peg/match pat text))
(assert (= (not should-match) (not result)) text)) (assert (= (not should-match) (not result)) (string "check-match " text)))
(defn check-deep (defn check-deep
[pat text what] [pat text what]
(def result (peg/match pat text)) (def result (peg/match pat text))
(assert (deep= result what) text)) (assert (deep= result what) (string "check-deep " text)))
# Just numbers # Just numbers