From www@deja.com Sat Feb 26 02:44:12 2000 Message-ID: <897f0n$n2d$1@nnrp1.deja.com> From: oleg@pobox.com Subject: Re: exception handling in scheme Date: Sat, 26 Feb 2000 02:50:00 GMT Reply-To: oleg@pobox.com Newsgroups: comp.lang.scheme References: X-Article-Creation-Date: Sat Feb 26 02:50:00 2000 GMT Content-Length: 3485 Status: OR In article , David Rush wrote: > > This (calling a thunk [upon signal arrival]) does seem > to be common, but it would seem to > make more sense to provide the current continuation as a parameter to > the signal handler. Then we could really write thread scheduling > systems and the like directly in Scheme... > It appears thunk as a signal handler will suffice. When a signal handler normally returns, the interrupted computation resumes. The signal handler can therefore use its own continuation for thread switching purposes. Listing 1 below shows the example of this idea. It uses non-preemptive "interrupts" and thus should run on every Scheme system. However, if you happen to have a Gambit-C system, you can insert Listing 2 into Listing 1 where indicated. This buys you a _preemptive_ re-scheduling upon arrival of a user-defined signal. You can do "kill -USR1 pid" to cause switching from one thread to another. Listing 1 ; A thread is a procedure of one argument (which ought to be disregarded) (define (make-thread pid) (define (fib n) (if (< n 2) 1 (+ (fib (- n 1)) (fib (- n 2))))) (lambda (dummy) (do () (#f) (display "Thread ") (display pid) (newline) (display "\tcomputed: ") (display (fib 27)) ; do some useless calculation (newline) (check-interrupt)))) ; Check for the interrupt and handle it, if any ; A global thunk 'user-interrupt' is entered to ; process the interrupt ; The present implementation always handles 'the interrupt' (define (check-interrupt) (user-interrupt)) ; Jobqueue is (or will be) a circular list of continuations ; for the threads. ; jobqueue points to the current thread (define jobqueue #f) ; yield to another thread. This implements a round-robin ; scheduling ; Note, jobqueue is a _circular_ list... (define (yield) (display "\nrescheduling...\n") (call-with-current-continuation (lambda (k) (set-car! jobqueue k) (set! jobqueue (cdr jobqueue)) ((car jobqueue) #f)))) (define user-interrupt yield) ; If you use Gambit-C, insert Listing 2 here ; To start it off... (set! jobqueue (list (make-thread 1) (make-thread 2))) (set-cdr! (cdr jobqueue) jobqueue) ((car jobqueue) #f) ; run the first thread... Listing 2 ; The following is Gambit-specific (c-declare "#include typedef void (*SIGNAL_HANDLER)(int); static void our_signal_handler(int x) { ___raise_interrupt (___INTR_USER); } ") (define intern-os-signal ; signal number... (c-lambda (unsigned-int) void "sigset(___arg1,our_signal_handler);")) (set! check-interrupt (lambda () #f)) ; unnecessary in our case.... ; as we use pre-emptive interrupts now.... (set! ##user-interrupt yield) (intern-os-signal 16) ; catching SIGUSR1 ; end-of-Gambit-specific code... To compile, save Listing 1 with inserted Listing 2 into a file simple-multi-threading.scm. Then do: gsc simple-multi-threading.scm gcc simple-multi-threading.c simple-multi-threading_.c -o simple-multi-threading -lgambc -ldl -lm ./simple-multi-threading & kill -USR1 pid # to cause rescheduling... Note, using of 'sigset' in Listing 2 is Solaris-specific. You may need to replace 'sigset' with 'signal' on other platforms. Option '-ldl' on the gcc command line is also Solaris-specific. On other OSes, you may need to use '-ldld' or nothing at all.