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:
./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)
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;
#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 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_saves(x) ((x)->value.context.saves)
#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_last_fp(x) ((x)->value.context.last_fp)
#define sexp_context_refuel(x) ((x)->value.context.refuel)

View file

@ -8,7 +8,8 @@
(if (thread-timeout?)
(if (and (pair? o) (pair? (cdr o)))
(cadr o)
(error "timed out waiting for thread" thread)))))))
(error "timed out waiting for thread" thread))
#t)))))
(define (thread-terminate! thread)
(if (%thread-terminate! thread) ;; need to yield if terminating ourself
@ -21,11 +22,18 @@
(define (mutex-lock! mutex . o)
(let ((timeout (and (pair? o) (car o)))
(thread (if (and (pair? o) (pair? (cdr o))) (cadr o) #t)))
(if (not (%mutex-lock! mutex timeout thread))
(thread-yield!))))
(cond ((%mutex-lock! mutex timeout thread))
(else
(thread-yield!)
(not (thread-timeout?))))))
(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 time? timeval?)

View file

@ -5,6 +5,7 @@
#include <chibi/eval.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
#define sexp_mutex_name(x) sexp_slot_ref(x, 0)
#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 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; */
@ -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 cell;
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))) {
cell = sexp_cons(ctx, thread, SEXP_NULL);
sexp_cdr(sexp_global(ctx, SEXP_G_THREADS_BACK)) = cell;
sexp_global(ctx, SEXP_G_THREADS_BACK) = cell;
} else { /* init queue */
sexp_global(ctx, SEXP_G_THREADS_BACK) = sexp_global(ctx, SEXP_G_THREADS_FRONT)
= sexp_cons(ctx, thread, SEXP_NULL);
sexp_global(ctx, SEXP_G_THREADS_BACK) = sexp_global(ctx, SEXP_G_THREADS_FRONT) = cell;
}
return SEXP_VOID;
}
@ -115,14 +115,15 @@ static void sexp_insert_timed (sexp ctx, sexp thread, sexp timeout) {
double d;
#endif
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)) {
sexp_context_timeval(ctx).tv_sec = sexp_unbox_fixnum(timeout);
sexp_context_timeval(ctx).tv_usec = 0;
sexp_context_timeval(ctx).tv_sec += sexp_unbox_fixnum(timeout);
#if SEXP_USE_FLONUMS
} else if (sexp_flonump(timeout)) {
d = sexp_flonum_value(timeout);
sexp_context_timeval(ctx).tv_sec = trunc(d);
sexp_context_timeval(ctx).tv_usec = (d-trunc(d))*1000000;
sexp_context_timeval(ctx).tv_sec += trunc(d);
sexp_context_timeval(ctx).tv_usec += (d-trunc(d))*1000000;
#endif
} else {
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_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;
}
sexp_context_timeoutp(ctx) = 0;
sexp_context_waitp(ctx) = 1;
sexp_context_event(ctx) = thread;
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 ls1, ls2;
if (sexp_not(condvar)) {
/* normal unlock */
/* normal unlock - always succeeds, just need to unblock threads */
if (sexp_truep(sexp_mutex_lockp(mutex))) {
sexp_mutex_lockp(mutex) = SEXP_FALSE;
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 {
/* 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 *************************/
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) {
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 *******************************/
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) {
struct timeval tval;
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 (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) {
sexp_context_waitp(ctx) = 0;
sexp_context_waitp(sexp_car(ls2)) = 0;
sexp_context_timeoutp(sexp_car(ls2)) = 0;
if (ls1==SEXP_NULL)
sexp_global(ctx, SEXP_G_THREADS_PAUSED) = paused = sexp_cdr(ls2);
else
sexp_cdr(ls1) = sexp_cdr(ls2);
tmp = sexp_cdr(ls2);
sexp_cdr(ls2) = front;
sexp_global(ctx, SEXP_G_THREADS_FRONT) = front = ls2;
sexp_cdr(ls2) = SEXP_NULL;
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;
} else {
ls1 = ls2;
ls2 = sexp_cdr(ls2);
}
}
}
/* TODO: check threads blocked on I/O */
/* ... */
/* check timeouts (must be _after_ previous checks) */
/* check timeouts */
if (sexp_pairp(paused)) {
if (gettimeofday(&tval, NULL) == 0) {
ls1 = SEXP_NULL;
@ -253,9 +308,14 @@ sexp sexp_scheduler (sexp ctx sexp_api_params(self, n), sexp root_thread) {
ls2 = sexp_cdr(ls2);
}
if (sexp_pairp(ls1)) {
sexp_cdr(ls1) = front;
sexp_global(ctx, SEXP_G_THREADS_FRONT) = front = paused;
sexp_global(ctx, SEXP_G_THREADS_PAUSED) = ls2;
sexp_cdr(ls1) = SEXP_NULL;
if (! sexp_pairp(sexp_global(ctx, SEXP_G_THREADS_BACK))) {
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)) {
/* either terminated or paused */
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;
} else {
/* swap with front of queue */
@ -284,6 +344,13 @@ sexp sexp_scheduler (sexp ctx sexp_api_params(self, n), sexp root_thread) {
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;
}

View file

@ -1,7 +1,7 @@
static const char* reverse_opcode_names[] =
{"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",
"STACK-REF", "LOCAL-REF", "LOCAL-SET",
"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);
if ((! (ls && sexp_pairp(ls)))
&& sexp_exception_procedure(exn)
&& sexp_procedurep(sexp_exception_procedure(exn)))
ls = sexp_bytecode_source(sexp_procedure_code(sexp_exception_procedure(exn)));
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);
#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 ****************************/
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_context_tracep(ctx)) {
sexp_print_stack(ctx, stack, top, fp, SEXP_FALSE);
fprintf(stderr, "%s\n", (*ip<=SEXP_OP_NUM_OPCODES) ?
reverse_opcode_names[*ip] : "UNKNOWN");
fprintf(stderr, "%s ip: %p stack: %p top: %d fp: %d\n", (*ip<=SEXP_OP_NUM_OPCODES) ?
reverse_opcode_names[*ip] : "UNKNOWN", ip, stack, top, fp);
}
#endif
switch (*ip++) {
@ -515,7 +557,7 @@ sexp sexp_vm (sexp ctx, sexp proc) {
tmp1 = sexp_cdr(sexp_global(ctx, SEXP_G_ERR_HANDLER));
sexp_context_last_fp(ctx) = fp;
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+2] = self;
stack[top+3] = sexp_make_fixnum(fp);
@ -643,8 +685,10 @@ sexp sexp_vm (sexp ctx, sexp proc) {
fp = top-4;
break;
case SEXP_OP_FCALL0:
tmp1 = _WORD0;
_ALIGN_IP();
sexp_context_top(ctx) = top;
sexp_context_last_fp(ctx) = fp;
_PUSH(((sexp_proc1)sexp_opcode_func(_WORD0))(ctx sexp_api_pass(_WORD0, 0)));
ip += sizeof(sexp);
sexp_check_exception();
@ -1225,6 +1269,7 @@ sexp sexp_vm (sexp ctx, sexp proc) {
break;
case SEXP_OP_YIELD:
fuel = 0;
_PUSH(SEXP_VOID);
break;
case SEXP_OP_RET:
i = sexp_unbox_fixnum(stack[fp]);