srfi-18 updates

This commit is contained in:
Alex Shinn 2010-07-11 05:57:07 +00:00
parent 061458f521
commit 1ecc2bb55c
9 changed files with 217 additions and 75 deletions

View file

@ -161,6 +161,9 @@ test-basic: chibi-scheme$(EXE)
test-build: test-build:
./tests/build/build-tests.sh ./tests/build/build-tests.sh
test-threads: chibi-scheme$(EXE)
LD_LIBRARY_PATH=".:$(LD_LIBRARY_PATH)" ./chibi-scheme$(EXE) tests/thread-tests.scm
test-numbers: chibi-scheme$(EXE) test-numbers: chibi-scheme$(EXE)
LD_LIBRARY_PATH=".:$(LD_LIBRARY_PATH)" ./chibi-scheme$(EXE) tests/numeric-tests.scm LD_LIBRARY_PATH=".:$(LD_LIBRARY_PATH)" ./chibi-scheme$(EXE) tests/numeric-tests.scm

40
eval.c
View file

@ -8,46 +8,6 @@
static int scheme_initialized_p = 0; static int scheme_initialized_p = 0;
#if SEXP_USE_DEBUG_VM
static void sexp_print_stack (sexp ctx, sexp *stack, int top, int fp, sexp out) {
int i;
if (! sexp_oportp(out)) out = sexp_current_error_port(ctx);
for (i=0; i<top; i++) {
sexp_printf(ctx, out, "%s %02d: ", ((i==fp) ? "*" : " "), i);
sexp_write(ctx, stack[i], out);
sexp_printf(ctx, out, "\n");
}
}
#endif
void sexp_stack_trace (sexp ctx, sexp out) {
int i, fp=sexp_context_last_fp(ctx);
sexp self, bc, ls, *stack=sexp_stack_data(sexp_context_stack(ctx));
if (! sexp_oportp(out)) out = sexp_current_error_port(ctx);
for (i=fp; i>4; i=sexp_unbox_fixnum(stack[i+3])) {
self = stack[i+2];
if (sexp_procedurep(self)) {
sexp_write_string(ctx, " called from ", out);
bc = sexp_procedure_code(self);
if (sexp_truep(sexp_bytecode_name(bc)))
sexp_write(ctx, sexp_bytecode_name(bc), out);
else
sexp_printf(ctx, out, "anon: %p", bc);
if ((ls=sexp_bytecode_source(bc)) && sexp_pairp(ls)) {
if (sexp_fixnump(sexp_cdr(ls)) && (sexp_cdr(ls) >= SEXP_ZERO)) {
sexp_write_string(ctx, " on line ", out);
sexp_write(ctx, sexp_cdr(ls), out);
}
if (sexp_stringp(sexp_car(ls))) {
sexp_write_string(ctx, " of file ", out);
sexp_write_string(ctx, sexp_string_data(sexp_car(ls)), out);
}
}
sexp_write_char(ctx, '\n', out);
}
}
}
static sexp analyze (sexp ctx, sexp x); static sexp analyze (sexp ctx, sexp x);
static void generate (sexp ctx, sexp x); static void generate (sexp ctx, sexp x);

View file

@ -694,7 +694,7 @@ SEXP_API sexp sexp_make_unsigned_integer(sexp ctx, sexp_luint_t x);
#define sexp_context_parent(x) ((x)->value.context.parent) #define sexp_context_parent(x) ((x)->value.context.parent)
#define sexp_context_saves(x) ((x)->value.context.saves) #define sexp_context_saves(x) ((x)->value.context.saves)
#define sexp_context_tailp(x) ((x)->value.context.tailp) #define sexp_context_tailp(x) ((x)->value.context.tailp)
#define sexp_context_tracep(x) ((x)->value.context.tailp) #define sexp_context_tracep(x) ((x)->value.context.tracep)
#define sexp_context_globals(x) ((x)->value.context.globals) #define sexp_context_globals(x) ((x)->value.context.globals)
#define sexp_context_last_fp(x) ((x)->value.context.last_fp) #define sexp_context_last_fp(x) ((x)->value.context.last_fp)
#define sexp_context_refuel(x) ((x)->value.context.refuel) #define sexp_context_refuel(x) ((x)->value.context.refuel)

View file

@ -8,7 +8,8 @@
(if (thread-timeout?) (if (thread-timeout?)
(if (and (pair? o) (pair? (cdr o))) (if (and (pair? o) (pair? (cdr o)))
(cadr o) (cadr o)
(error "timed out waiting for thread" thread))))))) (error "timed out waiting for thread" thread))
#t)))))
(define (thread-terminate! thread) (define (thread-terminate! thread)
(if (%thread-terminate! thread) ;; need to yield if terminating ourself (if (%thread-terminate! thread) ;; need to yield if terminating ourself
@ -21,11 +22,18 @@
(define (mutex-lock! mutex . o) (define (mutex-lock! mutex . o)
(let ((timeout (and (pair? o) (car o))) (let ((timeout (and (pair? o) (car o)))
(thread (if (and (pair? o) (pair? (cdr o))) (cadr o) #t))) (thread (if (and (pair? o) (pair? (cdr o))) (cadr o) #t)))
(if (not (%mutex-lock! mutex timeout thread)) (cond ((%mutex-lock! mutex timeout thread))
(thread-yield!)))) (else
(thread-yield!)
(not (thread-timeout?))))))
(define (mutex-unlock! mutex . o) (define (mutex-unlock! mutex . o)
#f) (let ((condvar (and (pair? o) (car o)))
(timeout (if (and (pair? o) (pair? (cdr o))) (cadr o) #f)))
(cond ((%mutex-unlock! mutex condvar timeout))
(else
(thread-yield!)
(not (thread-timeout?))))))
(define current-time get-time-of-day) (define current-time get-time-of-day)
(define time? timeval?) (define time? timeval?)

View file

@ -5,6 +5,7 @@
#include <chibi/eval.h> #include <chibi/eval.h>
#include <time.h> #include <time.h>
#include <sys/time.h> #include <sys/time.h>
#include <unistd.h>
#define sexp_mutex_name(x) sexp_slot_ref(x, 0) #define sexp_mutex_name(x) sexp_slot_ref(x, 0)
#define sexp_mutex_specific(x) sexp_slot_ref(x, 1) #define sexp_mutex_specific(x) sexp_slot_ref(x, 1)
@ -16,7 +17,7 @@
#define sexp_condvar_threads(x) sexp_slot_ref(x, 2) #define sexp_condvar_threads(x) sexp_slot_ref(x, 2)
#define timeval_le(a, b) (((a).tv_sec < (b).tv_sec) || (((a).tv_sec == (b).tv_sec) && ((a).tv_usec < (b).tv_usec))) #define timeval_le(a, b) (((a).tv_sec < (b).tv_sec) || (((a).tv_sec == (b).tv_sec) && ((a).tv_usec < (b).tv_usec)))
#define sexp_context_before(c, t) ((sexp_context_timeval(c).tv_sec != 0) && timeval_le(sexp_context_timeval(c), t)) #define sexp_context_before(c, t) (((sexp_context_timeval(c).tv_sec != 0) || (sexp_context_timeval(c).tv_usec != 0)) && timeval_le(sexp_context_timeval(c), t))
/* static int mutex_id, condvar_id; */ /* static int mutex_id, condvar_id; */
@ -72,13 +73,12 @@ sexp sexp_make_thread (sexp ctx sexp_api_params(self, n), sexp thunk, sexp name)
sexp sexp_thread_start (sexp ctx sexp_api_params(self, n), sexp thread) { sexp sexp_thread_start (sexp ctx sexp_api_params(self, n), sexp thread) {
sexp cell; sexp cell;
sexp_assert_type(ctx, sexp_contextp, SEXP_CONTEXT, thread); sexp_assert_type(ctx, sexp_contextp, SEXP_CONTEXT, thread);
cell = sexp_cons(ctx, thread, SEXP_NULL);
if (sexp_pairp(sexp_global(ctx, SEXP_G_THREADS_BACK))) { if (sexp_pairp(sexp_global(ctx, SEXP_G_THREADS_BACK))) {
cell = sexp_cons(ctx, thread, SEXP_NULL);
sexp_cdr(sexp_global(ctx, SEXP_G_THREADS_BACK)) = cell; sexp_cdr(sexp_global(ctx, SEXP_G_THREADS_BACK)) = cell;
sexp_global(ctx, SEXP_G_THREADS_BACK) = cell; sexp_global(ctx, SEXP_G_THREADS_BACK) = cell;
} else { /* init queue */ } else { /* init queue */
sexp_global(ctx, SEXP_G_THREADS_BACK) = sexp_global(ctx, SEXP_G_THREADS_FRONT) sexp_global(ctx, SEXP_G_THREADS_BACK) = sexp_global(ctx, SEXP_G_THREADS_FRONT) = cell;
= sexp_cons(ctx, thread, SEXP_NULL);
} }
return SEXP_VOID; return SEXP_VOID;
} }
@ -115,14 +115,15 @@ static void sexp_insert_timed (sexp ctx, sexp thread, sexp timeout) {
double d; double d;
#endif #endif
sexp ls1=SEXP_NULL, ls2=sexp_global(ctx, SEXP_G_THREADS_PAUSED); sexp ls1=SEXP_NULL, ls2=sexp_global(ctx, SEXP_G_THREADS_PAUSED);
if (sexp_integerp(timeout) || sexp_flonump(timeout))
gettimeofday(&sexp_context_timeval(ctx), NULL);
if (sexp_integerp(timeout)) { if (sexp_integerp(timeout)) {
sexp_context_timeval(ctx).tv_sec = sexp_unbox_fixnum(timeout); sexp_context_timeval(ctx).tv_sec += sexp_unbox_fixnum(timeout);
sexp_context_timeval(ctx).tv_usec = 0;
#if SEXP_USE_FLONUMS #if SEXP_USE_FLONUMS
} else if (sexp_flonump(timeout)) { } else if (sexp_flonump(timeout)) {
d = sexp_flonum_value(timeout); d = sexp_flonum_value(timeout);
sexp_context_timeval(ctx).tv_sec = trunc(d); sexp_context_timeval(ctx).tv_sec += trunc(d);
sexp_context_timeval(ctx).tv_usec = (d-trunc(d))*1000000; sexp_context_timeval(ctx).tv_usec += (d-trunc(d))*1000000;
#endif #endif
} else { } else {
sexp_context_timeval(ctx).tv_sec = 0; sexp_context_timeval(ctx).tv_sec = 0;
@ -143,8 +144,10 @@ static void sexp_insert_timed (sexp ctx, sexp thread, sexp timeout) {
sexp sexp_thread_join (sexp ctx sexp_api_params(self, n), sexp thread, sexp timeout) { sexp sexp_thread_join (sexp ctx sexp_api_params(self, n), sexp thread, sexp timeout) {
sexp_assert_type(ctx, sexp_contextp, SEXP_CONTEXT, thread); sexp_assert_type(ctx, sexp_contextp, SEXP_CONTEXT, thread);
if (sexp_context_refuel(thread) <= 0) /* return true if already terminated */ if (sexp_context_refuel(thread) <= 0) /* return true if already terminated */ {
return SEXP_TRUE; return SEXP_TRUE;
}
sexp_context_timeoutp(ctx) = 0;
sexp_context_waitp(ctx) = 1; sexp_context_waitp(ctx) = 1;
sexp_context_event(ctx) = thread; sexp_context_event(ctx) = thread;
sexp_insert_timed(ctx, ctx, timeout); sexp_insert_timed(ctx, ctx, timeout);
@ -188,31 +191,79 @@ sexp sexp_mutex_lock (sexp ctx sexp_api_params(self, n), sexp mutex, sexp timeou
} }
sexp sexp_mutex_unlock (sexp ctx sexp_api_params(self, n), sexp mutex, sexp condvar, sexp timeout) { sexp sexp_mutex_unlock (sexp ctx sexp_api_params(self, n), sexp mutex, sexp condvar, sexp timeout) {
sexp ls1, ls2;
if (sexp_not(condvar)) { if (sexp_not(condvar)) {
/* normal unlock */ /* normal unlock - always succeeds, just need to unblock threads */
if (sexp_truep(sexp_mutex_lockp(mutex))) { if (sexp_truep(sexp_mutex_lockp(mutex))) {
sexp_mutex_lockp(mutex) = SEXP_FALSE; sexp_mutex_lockp(mutex) = SEXP_FALSE;
sexp_mutex_thread(mutex) = ctx; sexp_mutex_thread(mutex) = ctx;
/* XXXX search for threads blocked on this mutex */ /* search for threads blocked on this mutex */
for (ls1=SEXP_NULL, ls2=sexp_global(ctx, SEXP_G_THREADS_PAUSED);
sexp_pairp(ls2); ls1=ls2, ls2=sexp_cdr(ls2))
if (sexp_context_event(sexp_car(ls2)) == mutex) {
if (ls1==SEXP_NULL)
sexp_global(ctx, SEXP_G_THREADS_PAUSED) = sexp_cdr(ls2);
else
sexp_cdr(ls1) = sexp_cdr(ls2);
sexp_cdr(ls2) = sexp_global(ctx, SEXP_G_THREADS_FRONT);
sexp_global(ctx, SEXP_G_THREADS_FRONT) = ls2;
if (! sexp_pairp(sexp_cdr(ls2)))
sexp_global(ctx, SEXP_G_THREADS_BACK) = ls2;
sexp_context_waitp(sexp_car(ls2))
= sexp_context_timeoutp(sexp_car(ls2)) = 0;
break;
}
} }
return SEXP_TRUE;
} else { } else {
/* wait on condition var */ /* wait on condition var */
sexp_context_waitp(ctx) = 1;
sexp_context_event(ctx) = condvar;
sexp_insert_timed(ctx, ctx, timeout);
return SEXP_FALSE;
} }
} }
/**************************** condition variables *************************/ /**************************** condition variables *************************/
sexp sexp_condition_variable_signal (sexp ctx sexp_api_params(self, n), sexp condvar) { sexp sexp_condition_variable_signal (sexp ctx sexp_api_params(self, n), sexp condvar) {
return SEXP_VOID; sexp ls1=SEXP_NULL, ls2=sexp_global(ctx, SEXP_G_THREADS_PAUSED);
for ( ; sexp_pairp(ls2); ls1=ls2, ls2=sexp_cdr(ls2))
if (sexp_context_event(sexp_car(ls2)) == condvar) {
if (ls1==SEXP_NULL)
sexp_global(ctx, SEXP_G_THREADS_PAUSED) = sexp_cdr(ls2);
else
sexp_cdr(ls1) = sexp_cdr(ls2);
sexp_cdr(ls2) = sexp_global(ctx, SEXP_G_THREADS_FRONT);
sexp_global(ctx, SEXP_G_THREADS_FRONT) = ls2;
if (! sexp_pairp(sexp_cdr(ls2)))
sexp_global(ctx, SEXP_G_THREADS_BACK) = ls2;
sexp_context_waitp(sexp_car(ls2)) = sexp_context_timeoutp(sexp_car(ls2)) = 0;
return SEXP_TRUE;
}
return SEXP_FALSE;
} }
sexp sexp_condition_variable_broadcast (sexp ctx sexp_api_params(self, n), sexp condvar) { sexp sexp_condition_variable_broadcast (sexp ctx sexp_api_params(self, n), sexp condvar) {
return SEXP_VOID; sexp res = SEXP_FALSE;
while (sexp_truep(sexp_condition_variable_signal(ctx, self, n, condvar)))
res = SEXP_TRUE;
return res;
} }
/**************************** the scheduler *******************************/ /**************************** the scheduler *******************************/
void sexp_wait_on_single_thread (sexp ctx) {
struct timeval tval;
useconds_t usecs = 0;
gettimeofday(&tval, NULL);
if (tval.tv_sec < sexp_context_timeval(ctx).tv_sec)
usecs = (sexp_context_timeval(ctx).tv_sec - tval.tv_sec) * 1000000;
if (tval.tv_usec < sexp_context_timeval(ctx).tv_usec)
usecs += sexp_context_timeval(ctx).tv_usec - tval.tv_usec;
usleep(usecs);
}
sexp sexp_scheduler (sexp ctx sexp_api_params(self, n), sexp root_thread) { sexp sexp_scheduler (sexp ctx sexp_api_params(self, n), sexp root_thread) {
struct timeval tval; struct timeval tval;
sexp res, ls1, ls2, tmp, paused, front=sexp_global(ctx, SEXP_G_THREADS_FRONT); sexp res, ls1, ls2, tmp, paused, front=sexp_global(ctx, SEXP_G_THREADS_FRONT);
@ -221,27 +272,31 @@ sexp sexp_scheduler (sexp ctx sexp_api_params(self, n), sexp root_thread) {
/* if we've terminated, check threads joining us */ /* if we've terminated, check threads joining us */
if (sexp_context_refuel(ctx) <= 0) { if (sexp_context_refuel(ctx) <= 0) {
for (ls1=SEXP_NULL, ls2=paused; sexp_pairp(ls2); ls2=sexp_cdr(ls2)) for (ls1=SEXP_NULL, ls2=paused; sexp_pairp(ls2); ) {
if (sexp_context_event(sexp_car(ls2)) == ctx) { if (sexp_context_event(sexp_car(ls2)) == ctx) {
sexp_context_waitp(ctx) = 0; sexp_context_waitp(sexp_car(ls2)) = 0;
sexp_context_timeoutp(sexp_car(ls2)) = 0;
if (ls1==SEXP_NULL) if (ls1==SEXP_NULL)
sexp_global(ctx, SEXP_G_THREADS_PAUSED) = paused = sexp_cdr(ls2); sexp_global(ctx, SEXP_G_THREADS_PAUSED) = paused = sexp_cdr(ls2);
else else
sexp_cdr(ls1) = sexp_cdr(ls2); sexp_cdr(ls1) = sexp_cdr(ls2);
tmp = sexp_cdr(ls2); tmp = sexp_cdr(ls2);
sexp_cdr(ls2) = front; sexp_cdr(ls2) = SEXP_NULL;
sexp_global(ctx, SEXP_G_THREADS_FRONT) = front = ls2; if (! sexp_pairp(sexp_global(ctx, SEXP_G_THREADS_BACK))) {
sexp_global(ctx, SEXP_G_THREADS_FRONT) = front = ls2;
} else {
sexp_cdr(sexp_global(ctx, SEXP_G_THREADS_BACK)) = ls2;
}
sexp_global(ctx, SEXP_G_THREADS_BACK) = ls2;
ls2 = tmp; ls2 = tmp;
} else { } else {
ls1 = ls2; ls1 = ls2;
ls2 = sexp_cdr(ls2); ls2 = sexp_cdr(ls2);
} }
}
} }
/* TODO: check threads blocked on I/O */ /* check timeouts */
/* ... */
/* check timeouts (must be _after_ previous checks) */
if (sexp_pairp(paused)) { if (sexp_pairp(paused)) {
if (gettimeofday(&tval, NULL) == 0) { if (gettimeofday(&tval, NULL) == 0) {
ls1 = SEXP_NULL; ls1 = SEXP_NULL;
@ -253,9 +308,14 @@ sexp sexp_scheduler (sexp ctx sexp_api_params(self, n), sexp root_thread) {
ls2 = sexp_cdr(ls2); ls2 = sexp_cdr(ls2);
} }
if (sexp_pairp(ls1)) { if (sexp_pairp(ls1)) {
sexp_cdr(ls1) = front; sexp_cdr(ls1) = SEXP_NULL;
sexp_global(ctx, SEXP_G_THREADS_FRONT) = front = paused; if (! sexp_pairp(sexp_global(ctx, SEXP_G_THREADS_BACK))) {
sexp_global(ctx, SEXP_G_THREADS_PAUSED) = ls2; sexp_global(ctx, SEXP_G_THREADS_FRONT) = front = paused;
} else {
sexp_cdr(sexp_global(ctx, SEXP_G_THREADS_BACK)) = paused;
}
sexp_global(ctx, SEXP_G_THREADS_BACK) = ls1;
sexp_global(ctx, SEXP_G_THREADS_PAUSED) = paused = ls2;
} }
} }
} }
@ -266,7 +326,7 @@ sexp sexp_scheduler (sexp ctx sexp_api_params(self, n), sexp root_thread) {
if ((sexp_context_refuel(ctx) <= 0) || sexp_context_waitp(ctx)) { if ((sexp_context_refuel(ctx) <= 0) || sexp_context_waitp(ctx)) {
/* either terminated or paused */ /* either terminated or paused */
sexp_global(ctx, SEXP_G_THREADS_FRONT) = sexp_cdr(front); sexp_global(ctx, SEXP_G_THREADS_FRONT) = sexp_cdr(front);
if (ctx == sexp_car(sexp_global(ctx, SEXP_G_THREADS_BACK))) if (! sexp_pairp(sexp_cdr(front)))
sexp_global(ctx, SEXP_G_THREADS_BACK) = SEXP_NULL; sexp_global(ctx, SEXP_G_THREADS_BACK) = SEXP_NULL;
} else { } else {
/* swap with front of queue */ /* swap with front of queue */
@ -284,6 +344,13 @@ sexp sexp_scheduler (sexp ctx sexp_api_params(self, n), sexp root_thread) {
res = ctx; res = ctx;
} }
if (sexp_context_waitp(res)) {
/* the only thread available was waiting */
sexp_wait_on_single_thread(res);
sexp_context_timeoutp(res) = 1;
sexp_context_waitp(res) = 0;
}
return res; return res;
} }

View file

@ -1,7 +1,7 @@
static const char* reverse_opcode_names[] = static const char* reverse_opcode_names[] =
{"NOOP", "RAISE", "RESUMECC", "CALLCC", "APPLY1", "TAIL-CALL", "CALL", {"NOOP", "RAISE", "RESUMECC", "CALLCC", "APPLY1", "TAIL-CALL", "CALL",
"FCALL0", "FCALL1", "FCALL2", "FCALL3", "FCALL4", "FCALL5", "FCALL6", "FCALL0", "FCALL1", "FCALL2", "FCALL3", "FCALL4", "FCALL5", "FCALL6", "FCALLN",
"JUMP-UNLESS", "JUMP", "PUSH", "DROP", "GLOBAL-REF", "GLOBAL-KNOWN-REF", "JUMP-UNLESS", "JUMP", "PUSH", "DROP", "GLOBAL-REF", "GLOBAL-KNOWN-REF",
"STACK-REF", "LOCAL-REF", "LOCAL-SET", "STACK-REF", "LOCAL-REF", "LOCAL-SET",
"CLOSURE-REF", "VECTOR-REF", "VECTOR-SET", "VECTOR-LENGTH", "STRING-REF", "CLOSURE-REF", "VECTOR-REF", "VECTOR-SET", "VECTOR-LENGTH", "STRING-REF",

1
sexp.c
View file

@ -421,6 +421,7 @@ sexp sexp_print_exception_op (sexp ctx sexp_api_params(self, n), sexp exn, sexp
} }
ls = sexp_exception_source(exn); ls = sexp_exception_source(exn);
if ((! (ls && sexp_pairp(ls))) if ((! (ls && sexp_pairp(ls)))
&& sexp_exception_procedure(exn)
&& sexp_procedurep(sexp_exception_procedure(exn))) && sexp_procedurep(sexp_exception_procedure(exn)))
ls = sexp_bytecode_source(sexp_procedure_code(sexp_exception_procedure(exn))); ls = sexp_bytecode_source(sexp_procedure_code(sexp_exception_procedure(exn)));
if (ls && sexp_pairp(ls)) { if (ls && sexp_pairp(ls)) {

58
tests/thread-tests.scm Normal file
View file

@ -0,0 +1,58 @@
(import (srfi 18))
(define *tests-run* 0)
(define *tests-passed* 0)
(define-syntax test
(syntax-rules ()
((test name expr expect)
(begin
(set! *tests-run* (+ *tests-run* 1))
(let ((str (call-with-output-string (lambda (out) (display name out))))
(res expr))
(display str)
(write-char #\space)
(display (make-string (max 0 (- 72 (string-length str))) #\.))
(flush-output)
(cond
((equal? res expect)
(set! *tests-passed* (+ *tests-passed* 1))
(display " [PASS]\n"))
(else
(display " [FAIL]\n")
(display " expected ") (write expect)
(display " but got ") (write res) (newline))))))))
(define (test-report)
(write *tests-passed*)
(display " out of ")
(write *tests-run*)
(display " passed (")
(write (* (/ *tests-passed* *tests-run*) 100))
(display "%)")
(newline))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; run tests
(test "no threads" (begin 'ok) 'ok)
(test "unstarted thread" (let ((t (make-thread (lambda () (error "oops"))))) 'ok) 'ok)
(test "ignored thread terminates" (let ((t (make-thread (lambda () 'oops)))) (thread-start! t) 'ok) 'ok)
(test "ignored thread hangs" (let ((t (make-thread (lambda () (let lp () (lp)))))) (thread-start! t) 'ok) 'ok)
(test "joined thread terminates" (let ((t (make-thread (lambda () 'oops)))) (thread-start! t) (thread-join! t) 'ok) 'ok)
(test "joined thread hangs, timeout" (let ((t (make-thread (lambda () (let lp () (lp)))))) (thread-start! t) (thread-join! t 1 'timeout)) 'timeout)
(test "basic mutex" (let ((m (make-mutex))) (and (mutex? m) 'ok)) 'ok)
(test "mutex unlock" (let ((m (make-mutex))) (and (mutex-unlock! m) 'ok)) 'ok)
(test "mutex lock/unlock" (let ((m (make-mutex))) (and (mutex-lock! m) (mutex-unlock! m) 'ok)) 'ok)
(test "mutex lock timeout" (let* ((m (make-mutex)) (t (make-thread (lambda () (mutex-lock! m))))) (thread-start! t) (thread-yield!) (if (mutex-lock! m 1) 'fail 'timeout)) 'timeout)
;(test "basic condition-variable" () 'ok)
;(test "condition-variable signal" () 'ok)
;(test "condition-variable broadcast" () 'ok)
;(test "mailbox")
(test-report)

51
vm.c
View file

@ -4,6 +4,48 @@
static sexp sexp_apply1 (sexp ctx, sexp f, sexp x); static sexp sexp_apply1 (sexp ctx, sexp f, sexp x);
#if SEXP_USE_DEBUG_VM > 1
static void sexp_print_stack (sexp ctx, sexp *stack, int top, int fp, sexp out) {
int i;
if (! sexp_oportp(out)) out = sexp_current_error_port(ctx);
for (i=0; i<top; i++) {
sexp_printf(ctx, out, "%s %02d: ", ((i==fp) ? "*" : " "), i);
sexp_write(ctx, stack[i], out);
sexp_printf(ctx, out, "\n");
}
}
#else
#define sexp_print_stack(ctx, stacl, top, fp, out)
#endif
void sexp_stack_trace (sexp ctx, sexp out) {
int i, fp=sexp_context_last_fp(ctx);
sexp self, bc, ls, *stack=sexp_stack_data(sexp_context_stack(ctx));
if (! sexp_oportp(out)) out = sexp_current_error_port(ctx);
for (i=fp; i>4; i=sexp_unbox_fixnum(stack[i+3])) {
self = stack[i+2];
if (sexp_procedurep(self)) {
sexp_write_string(ctx, " called from ", out);
bc = sexp_procedure_code(self);
if (sexp_truep(sexp_bytecode_name(bc)))
sexp_write(ctx, sexp_bytecode_name(bc), out);
else
sexp_printf(ctx, out, "anon: %p", bc);
if ((ls=sexp_bytecode_source(bc)) && sexp_pairp(ls)) {
if (sexp_fixnump(sexp_cdr(ls)) && (sexp_cdr(ls) >= SEXP_ZERO)) {
sexp_write_string(ctx, " on line ", out);
sexp_write(ctx, sexp_cdr(ls), out);
}
if (sexp_stringp(sexp_car(ls))) {
sexp_write_string(ctx, " of file ", out);
sexp_write_string(ctx, sexp_string_data(sexp_car(ls)), out);
}
}
sexp_write_char(ctx, '\n', out);
}
}
}
/************************* code generation ****************************/ /************************* code generation ****************************/
static void emit_word (sexp ctx, sexp_uint_t val) { static void emit_word (sexp ctx, sexp_uint_t val) {
@ -503,8 +545,8 @@ sexp sexp_vm (sexp ctx, sexp proc) {
#if SEXP_USE_DEBUG_VM #if SEXP_USE_DEBUG_VM
if (sexp_context_tracep(ctx)) { if (sexp_context_tracep(ctx)) {
sexp_print_stack(ctx, stack, top, fp, SEXP_FALSE); sexp_print_stack(ctx, stack, top, fp, SEXP_FALSE);
fprintf(stderr, "%s\n", (*ip<=SEXP_OP_NUM_OPCODES) ? fprintf(stderr, "%s ip: %p stack: %p top: %d fp: %d\n", (*ip<=SEXP_OP_NUM_OPCODES) ?
reverse_opcode_names[*ip] : "UNKNOWN"); reverse_opcode_names[*ip] : "UNKNOWN", ip, stack, top, fp);
} }
#endif #endif
switch (*ip++) { switch (*ip++) {
@ -515,7 +557,7 @@ sexp sexp_vm (sexp ctx, sexp proc) {
tmp1 = sexp_cdr(sexp_global(ctx, SEXP_G_ERR_HANDLER)); tmp1 = sexp_cdr(sexp_global(ctx, SEXP_G_ERR_HANDLER));
sexp_context_last_fp(ctx) = fp; sexp_context_last_fp(ctx) = fp;
if (! sexp_procedurep(tmp1)) goto end_loop; if (! sexp_procedurep(tmp1)) goto end_loop;
stack[top] = (sexp) 1; stack[top] = SEXP_ONE;
stack[top+1] = sexp_make_fixnum(ip-sexp_bytecode_data(bc)); stack[top+1] = sexp_make_fixnum(ip-sexp_bytecode_data(bc));
stack[top+2] = self; stack[top+2] = self;
stack[top+3] = sexp_make_fixnum(fp); stack[top+3] = sexp_make_fixnum(fp);
@ -643,8 +685,10 @@ sexp sexp_vm (sexp ctx, sexp proc) {
fp = top-4; fp = top-4;
break; break;
case SEXP_OP_FCALL0: case SEXP_OP_FCALL0:
tmp1 = _WORD0;
_ALIGN_IP(); _ALIGN_IP();
sexp_context_top(ctx) = top; sexp_context_top(ctx) = top;
sexp_context_last_fp(ctx) = fp;
_PUSH(((sexp_proc1)sexp_opcode_func(_WORD0))(ctx sexp_api_pass(_WORD0, 0))); _PUSH(((sexp_proc1)sexp_opcode_func(_WORD0))(ctx sexp_api_pass(_WORD0, 0)));
ip += sizeof(sexp); ip += sizeof(sexp);
sexp_check_exception(); sexp_check_exception();
@ -1225,6 +1269,7 @@ sexp sexp_vm (sexp ctx, sexp proc) {
break; break;
case SEXP_OP_YIELD: case SEXP_OP_YIELD:
fuel = 0; fuel = 0;
_PUSH(SEXP_VOID);
break; break;
case SEXP_OP_RET: case SEXP_OP_RET:
i = sexp_unbox_fixnum(stack[fp]); i = sexp_unbox_fixnum(stack[fp]);