home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The World of Computer Software
/
World_Of_Computer_Software-02-385-Vol-1of3.iso
/
s
/
s48.zip
/
MISC
/
THREAD.SCM
< prev
next >
Wrap
Text File
|
1992-06-17
|
12KB
|
401 lines
; Copyright (c) 1992 by Richard Kelsey and Jonathan Rees. See file COPYING.
; Multitasking
; A thread has:
; - uid - no semantic content.
; - a primitive continuation, unless thread is current or finished
; - dynamic environment
; - run status (active, stopped, done) - used only for stop & start
; operations
; What are the invariants?
(define thread-type
(make-record-type 'thread
'(uid continuation dynamic-state status
queue name)))
(define thread-uid (record-accessor thread-type 'uid))
(define thread-continuation (record-accessor thread-type 'continuation))
(define thread-dynamic-state (record-accessor thread-type 'dynamic-state))
(define thread-status (record-accessor thread-type 'status))
(define thread-queue (record-accessor thread-type 'queue))
(define thread-name (record-accessor thread-type 'name));for debugging
(define set-thread-continuation! (record-modifier thread-type 'continuation))
(define set-thread-dynamic-state! (record-modifier thread-type 'dynamic-state))
(define set-thread-status! (record-modifier thread-type 'status))
(define set-thread-queue! (record-modifier thread-type 'queue))
(define thread? (record-predicate thread-type))
(define-syntax with-interrupts-inhibited
(syntax-rules ()
((with-interrupts-inhibited . body)
(begin (set-enabled-interrupts! 0)
(let ((result (begin . body)))
(set-enabled-interrupts! all-interrupts)
result)))))
; Spawn a new thread
(define (spawn thunk . name-option)
(let* ((name (if (null? name-option) #f (car name-option)))
(thread (make-thread thunk name)))
(start-thread thread)
thread))
(define *thread-uid* 1)
(define make-thread
(let ((make (record-constructor thread-type
'(uid continuation
dynamic-state
status queue name))))
(lambda (thunk name)
(let ((thread (make *thread-uid*
(compose-continuation
(lambda (ignore)
(thread-top-level thunk))
#f)
#f
'stopped
runnable-threads
name)))
(set-thread-dynamic-state!
thread
(make-dynamic-state thread (thread-initial-dynamic-env)))
(set! *thread-uid* (+ *thread-uid* 1))
thread))))
(define (thread-top-level thunk)
(set-enabled-interrupts! all-interrupts)
(thunk)
(terminate-current-thread))
(define (terminate-current-thread)
(set-enabled-interrupts! all-interrupts)
(travel-to-empty!) ;Unwind
(set-enabled-interrupts! 0)
(set-thread-status! (current-thread) 'done)
(set-thread-continuation! (current-thread) #f)
(schedule-thread (another-thread)))
(define (start-thread thread) ;Interrupts enabled
(with-interrupts-inhibited
(really-start-thread thread)))
(define (really-start-thread thread) ;Interrupts disabled
(if (eq? (thread-status thread) 'stopped)
(begin (set-thread-status! thread 'active)
(move-to-queue (current-thread) runnable-threads)
(switch-to-thread thread)
#t)
(eq? (thread-status thread) 'active)))
(define (stop-thread thread) ;Interrupts enabled
(with-interrupts-inhibited
(really-stop-thread thread)))
(define (really-stop-thread thread) ;Call with interrupts disabled
(if (eq? (thread-status thread) 'active)
(let ((q (thread-queue thread)))
(if q (delete-from-queue! q thread))
(set-thread-status! thread 'stopped)
(if (eq? thread (current-thread))
(suspend-this-thread))
#t)
(eq? (thread-status thread) 'stopped)))
(define (kill-thread thread) ;Interrupts enabled
(interrupt-thread thread terminate-current-thread))
(define (interrupt-thread thread thunk)
(with-interrupts-inhibited
(cond ((eq? thread (current-thread))
(let ((ei (set-enabled-interrupts! all-interrupts)))
(thunk)
(set-enabled-interrupts! ei)
#t))
((really-stop-thread thread)
(set-thread-queue! thread runnable-threads)
(set-thread-continuation!
thread
(compose-continuation
(lambda (ignore)
(set-enabled-interrupts! all-interrupts)
(thunk))
(thread-continuation thread)))
(really-start-thread thread))
(else #f))))
(define (move-to-queue thread q)
(set-thread-queue! thread q)
(enqueue q thread))
; --------------------
; Scheduler
(define runnable-threads (make-queue))
(define (make-ready! thread)
(move-to-queue thread runnable-threads))
(define (handle-alarmclock-interrupt ei)
(arrange-for-alarm-interrupt) ;Allow another one to come along
;; Interrupts are disabled at this point
(if (not (queue-empty? runnable-threads)) ;speed/consing hack
(relinquish-timeslice))
(set-enabled-interrupts! ei))
(define (suspend-this-thread) ;Call this with interrupts disabled
(switch-to-thread (another-thread)))
(define (switch-to-thread thread)
(primitive-catch ;(internal-catch (lambda (cont env) ...))
(lambda (cont)
(set-thread-continuation! (current-thread) cont)
(schedule-thread thread))))
(define (schedule-thread thread) ;Call with interrupts disabled
(set-dynamic-state! (thread-dynamic-state thread))
(with-continuation (thread-continuation (current-thread))
unspecified))
(define (another-thread) ;Call with interrupts disabled
(if (queue-empty? runnable-threads)
(if (and *keyboard-interrupt-thread*
(not (eq? (thread-status *keyboard-interrupt-thread*) 'done)))
(interrupt-thread *keyboard-interrupt-thread*
(lambda ()
(error "no threads to run")))
(halt 0))
(dequeue runnable-threads)))
(define (dispatch) ;Interrupts disabled
(make-ready! (current-thread))
(suspend-this-thread))
(define (relinquish-timeslice) ;Interrupts enabled
(with-interrupts-inhibited (dispatch)))
; --------------------
; Locks (= semaphores)
(define lock-type
(make-record-type 'lock '(identification owner queue)))
(define lock-identification (record-accessor lock-type 'identification))
(define lock-owner (record-accessor lock-type 'owner))
(define lock-queue (record-accessor lock-type 'queue))
(define set-lock-owner! (record-modifier lock-type 'owner))
(define lock? (record-predicate lock-type))
(define *lock-uid* 0)
(define make-lock
(let ((make (record-constructor lock-type
'(identification owner queue))))
(lambda ()
(let ((uid *lock-uid*))
(set! *lock-uid* (+ uid 1))
(make uid #f (make-queue))))))
(define (obtain-lock lock) ;Interrupts enabled
(with-interrupts-inhibited
(let loop ()
(if (let ((owner (lock-owner lock)))
(and owner
(not (eq? owner (current-thread)))
(not (eq? (thread-status owner) 'done))))
(begin (move-to-queue (current-thread)
(lock-queue lock))
(suspend-this-thread)
(loop))
(set-lock-owner! lock (current-thread))))))
(define (release-lock lock) ;Interrupts enabled
(if (eq? (lock-owner lock) (current-thread))
(with-interrupts-inhibited
(set-lock-owner! lock #f)
(if (not (queue-empty? (lock-queue lock)))
(begin (make-ready! (current-thread))
(switch-to-thread (dequeue (lock-queue lock))))))))
(define (with-lock lock thunk) ;Interrupts enabled
(if (eq? (lock-owner lock) (current-thread))
(thunk)
(dynamic-wind (lambda () (obtain-lock lock))
thunk
(lambda () (release-lock lock)))))
; --------------------
; Condition variables
(define (make-condvar)
(cons (make-queue) #f))
(define condvar-queue car) ; #f means variable has been set
(define condvar-value cdr)
(define (condvar-ref condvar) ;Interrupts enabled
(with-interrupts-inhibited
(let loop ()
(if (condvar-queue condvar)
(begin (move-to-queue (current-thread)
(condvar-queue condvar))
(suspend-this-thread)
(loop))
(condvar-value condvar)))))
(define (condvar-set! condvar value) ;Interrupts enabled
(if (with-interrupts-inhibited
(let ((q (condvar-queue condvar)))
(if q
(begin (set-cdr! condvar value)
(set-car! condvar #f)
(if (queue-empty? q)
#f
(let ((first (dequeue q)))
(let loop ()
(if (queue-empty? q)
(begin (make-ready! (current-thread))
(switch-to-thread first)
#f)
(begin (make-ready! (dequeue q))
(loop)))))))
;; OK to store the same value twice.
(not (eq? value (condvar-value condvar))))))
(error "invalid condvar-set!" (condvar-value condvar) value)))
(define (kill-condvar condvar)
(for-each kill-thread (with-interrupts-inhibited
(if (condvar-queue condvar)
(queue->list (condvar-queue condvar))
'()))))
; --------------------
; Input
(define (thread-read-char port)
(with-interrupts-inhibited
(let loop ()
(if (char-ready? port)
(read-char port)
(begin (dispatch)
(loop))))))
(define (thread-peek-char port)
(with-interrupts-inhibited
(let loop ()
(if (char-ready? port)
(peek-char port)
(begin (dispatch)
(loop))))))
; --------------------
; Initialization
(define (start-multitasking handler)
(set! *one-second* (vm-extension 1 #f))
(set! runnable-threads (make-queue))
(set! *thread-initial-dynamic-env*
(make-thread-initial-dynamic-env handler))
(let* ((this-thread (make-thread #f 'initial))
(state (make-dynamic-state this-thread
(get-dynamic-env))))
(set-thread-dynamic-state! this-thread state)
(set-thread-status! this-thread 'active)
(set-dynamic-state! state))
(vector-set! interrupt-handlers
interrupt/periodic
handle-alarmclock-interrupt)
(arrange-for-alarm-interrupt)
(let ((ei (set-enabled-interrupts! 0)))
(set! all-interrupts
(bitwise-ior ei (arithmetic-shift 1 interrupt/periodic)))
(set-enabled-interrupts! all-interrupts)))
(define (simple-thread-condition-handler)
(let ((port (current-output-port)))
(lambda (c h)
(cond ((warning? c)
(display-condition c port)
(unspecified)) ;Proceed
((interrupt? c)
(terminate-current-thread))
((error? c)
(display-condition c port)
(terminate-current-thread))
(else ;Proceed
(unspecified))))))
(define (make-thread-initial-dynamic-env handler)
(let ((in (current-input-port))
(out (current-output-port)))
(with-dynamic-env (empty-dynamic-env)
(lambda ()
(let-fluid $current-input-port in
(lambda ()
(let-fluid $current-output-port out
(lambda ()
(really-with-handler handler
get-dynamic-env)))))))))
(define (set-keyboard-interrupt-thread! thread)
(set! *keyboard-interrupt-thread* thread)
(vector-set! interrupt-handlers
interrupt/keyboard
(if thread
handle-keyboard-interrupt
(lambda (ei)
(set-enabled-interrupts! all-interrupts)
(error "interrupt not handled")))))
(define *keyboard-interrupt-thread* #f)
(define (handle-keyboard-interrupt ei)
(interrupt-thread *keyboard-interrupt-thread*
(lambda ()
(raise '(interrupt keyboard)))))
(define *thread-initial-dynamic-env* #f)
(define (thread-initial-dynamic-env)
*thread-initial-dynamic-env*)
(define (arrange-for-alarm-interrupt)
(vm-extension 3 *one-second*)) ;This causes an interrupt in 1 second
(define *one-second* 120)
(define all-interrupts 0)
(define (kill-all-other-threads) ;Interrupts enabled
(for-each kill-thread (with-interrupts-inhibited
(queue->list runnable-threads)))
(relinquish-timeslice))
(define-record-discloser thread-type
(lambda (thread)
(cons 'thread
(cons (thread-uid thread)
(let ((name (thread-name thread)))
(if name
(list name)
'()))))))
;(put 'with-interrupts-inhibited 'scheme-indent-hook 0)
;(put 'with-dynamic-env 'scheme-indent-hook 1)
;(put 'really-with-handler 'scheme-indent-hook 1)