123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407 |
- ; Copyright (c) 1993-2008 by Richard Kelsey and Jonathan Rees. See file COPYING.
- ; Code for handling interrupts.
- ; New interrupt handler vector in *val*
- (define-opcode set-interrupt-handlers!
- (cond ((or (not (vm-vector? *val*))
- (< (vm-vector-length *val*) interrupt-count))
- (raise-exception wrong-type-argument 0 *val*))
- (else
- (let ((temp (shared-ref *interrupt-handlers*)))
- (shared-set! *interrupt-handlers* *val*)
- (set! *val* temp)
- (goto continue 0)))))
- ; New interrupt mask as fixnum in *val*
- (define-opcode set-enabled-interrupts!
- (let ((old *enabled-interrupts*))
- (set-enabled-interrupts! (extract-fixnum *val*))
- (set! *val* (enter-fixnum old))
- (goto continue 0)))
- ; Save the current interpreter state and call an interrupt handler.
- (define (handle-interrupt)
- (push *val*)
- (receive (code pc)
- (current-code+pc)
- (push code)
- (push (enter-fixnum pc)))
- (push-interrupt-state)
- (push-adlib-continuation! (code+pc->code-pointer *interrupted-byte-opcode-return-code*
- return-code-pc))
- (goto find-and-call-interrupt-handler))
- (define (handle-native-interrupt protocol-skip)
- (push (enter-fixnum protocol-skip))
- (push *val*)
- (push-interrupt-state)
- (push-adlib-continuation! (code+pc->code-pointer *interrupted-native-call-return-code*
- return-code-pc))
- (goto find-and-call-interrupt-handler))
- (define (handle-native-poll template return-address)
- (push *val*)
- (push template)
- (push return-address)
- (push-interrupt-state)
- (push-adlib-continuation! (code+pc->code-pointer *native-poll-return-code*
- return-code-pc))
- (goto find-and-call-interrupt-handler))
- ; MG: This is an old comment, I don't want to remove it because I
- ; don't understand it:
- ; We now have three places interrupts are caught:
- ; - during a byte-code call
- ; - during a native-code call
- ; - during a native-code poll
- ; The two native calls can be done using the same method. Argh. We need
- ; to save the proposal and enabled interrupts and still end up with a template
- ; on top of the stack. Just make a return pointer with two extra pointer
- ; slots at the top. The native-dispatch code pops the code pointer and
- ; template, pushes the extra state, and then ... . No. The simple thing
- ; to do is have the native code make one continuation and then we push a
- ; second, byte-coded one on top. It's ugly no matter what.
- ; Ditto, except that we are going to return to the current continuation instead
- ; of continuating with the current template.
- (define interrupt-state-descriptors 2)
- (define (push-interrupt-state)
- (push (current-proposal))
- (set-current-proposal! false)
- (push (enter-fixnum *enabled-interrupts*)))
- (define (s48-pop-interrupt-state)
- (set-enabled-interrupts! (extract-fixnum (pop)))
- (set-current-proposal! (pop)))
- (define (find-and-call-interrupt-handler)
- (let* ((pending-interrupt (get-highest-priority-interrupt!))
- (handlers (shared-ref *interrupt-handlers*))
- (arg-count (push-interrupt-args pending-interrupt)))
- (if (not (vm-vector? handlers))
- (error "interrupt handler is not a vector"))
- (set! *val* (vm-vector-ref handlers pending-interrupt))
- (if (not (closure? *val*))
- (error "interrupt handler is not a closure" pending-interrupt))
- (set-enabled-interrupts! 0)
- (goto call-interrupt-handler arg-count pending-interrupt)))
- ; Push the correct arguments for each type of interrupt.
- ;
- ; For alarm interrupts the interrupted template is passed to the handler
- ; for use by code profilers.
- ; For gc interrupts we push the list of things to be finalized,
- ; the interrupt mask, and whether the GC is running out of space.
- ; For i/o-completion we push the channel and its status.
- ; For i/o-error we push the channel and the error code.
- ; For external-event, we push the event-type uid.
- (define (push-interrupt-args pending-interrupt)
- (cond ((eq? pending-interrupt (enum interrupt alarm))
- (push *interrupted-template*)
- (set! *interrupted-template* false)
- (push (enter-fixnum *enabled-interrupts*))
- 2)
- ((or (eq? pending-interrupt (enum interrupt post-major-gc))
- (eq? pending-interrupt (enum interrupt post-minor-gc)))
- (push *finalize-these*)
- (set! *finalize-these* null)
- (push (enter-fixnum *enabled-interrupts*))
- (push (enter-boolean *gc-in-trouble?*))
- 3)
- ((eq? pending-interrupt (enum interrupt i/o-completion))
- ;; we don't know which one it is for each individual channel
- (let ((channel (dequeue-channel!)))
- (if (not (channel-queue-empty?))
- (note-interrupt! (enum interrupt i/o-completion)))
- (push channel)
- (push (channel-error? channel))
- (push (channel-os-status channel))
- (push (enter-fixnum *enabled-interrupts*))
- 4))
- ((eq? pending-interrupt (enum interrupt os-signal))
- (push (enter-fixnum (os-signal-ring-remove!)))
- (if (os-signal-ring-ready?)
- (note-interrupt! (enum interrupt os-signal)))
- (push (enter-fixnum *enabled-interrupts*))
- 2)
- ((eq? pending-interrupt (enum interrupt external-event))
- (receive (uid still-ready?)
- (dequeue-external-event!)
- (push (enter-fixnum uid))
- (if still-ready?
- (note-interrupt! (enum interrupt external-event)))
- (push (enter-fixnum *enabled-interrupts*))
- 2))
- (else
- (push (enter-fixnum *enabled-interrupts*))
- 1)))
- ;;; Dealing with OS signals
- (define *os-signal-ring-length* 32)
- (define *os-signal-ring*
- (let ((v (make-vector *os-signal-ring-length* 0)))
- (if (null-pointer? v)
- (error "out of memory, unable to continue"))
- v))
- (define *os-signal-ring-start* 0) ; index of oldest signal
- (define *os-signal-ring-ready* 0) ; index of last signal for which an
- ; os-event has already been generated
- (define *os-signal-ring-end* 0) ; index of newest signal
- ;; ring-like incrementation
- (define-syntax os-signal-ring-inc!
- (syntax-rules ()
- ((os-signal-ring-inc! var)
- (set! var
- (if (= var
- (- *os-signal-ring-length* 1))
- 0
- (+ var 1))))))
- (define (os-signal-ring-ready?)
- (not (= *os-signal-ring-ready*
- *os-signal-ring-start*)))
- (define (os-signal-ring-add! sig)
- (os-signal-ring-inc! *os-signal-ring-end*)
- (if (= *os-signal-ring-start*
- *os-signal-ring-end*)
- (error "OS signal ring too small, report to Scheme 48 maintainers"))
- (vector-set! *os-signal-ring* *os-signal-ring-end* sig))
- (define (os-signal-ring-empty?)
- (= *os-signal-ring-start*
- *os-signal-ring-end*))
- (define (os-signal-ring-remove!)
- (if (os-signal-ring-empty?)
- (error "This cannot happen: OS signal ring empty"))
- (let ((sig (vector-ref *os-signal-ring* *os-signal-ring-start*)))
- (set! *os-signal-ring-start*
- (if (= *os-signal-ring-start*
- (- *os-signal-ring-length* 1))
- 0
- (+ *os-signal-ring-start* 1)))
- sig))
- ; Called from outside when an os-signal event is returned.
- (define (s48-add-os-signal sig)
- (os-signal-ring-add! sig))
- ; Called from outside to check whether an os-event has to be signalled
- (define (s48-os-signal-pending)
- (if (= *os-signal-ring-ready*
- *os-signal-ring-end*)
- #f
- (begin
- (os-signal-ring-inc! *os-signal-ring-ready*)
- #t)))
-
- ; Called from outside to initialize a new process.
- (define (s48-reset-interrupts!)
- (set! *os-signal-ring-start* 0)
- (set! *os-signal-ring-ready* 0)
- (set! *os-signal-ring-end* 0)
- (set! *enabled-interrupts* 0)
- (pending-interrupts-clear!)
- (set! s48-*pending-interrupt?* #f))
- (define-opcode poll
- (if (and (interrupt-flag-set?)
- (pending-interrupt?))
- (goto handle-interrupt)
- (goto continue 0)))
-
- (define-opcode resume-interrupted-opcode-to-byte-code
- (pop)
- (s48-pop-interrupt-state)
- (let ((pc (pop)))
- (set-code-pointer! (pop) (extract-fixnum pc)))
- (set! *val* (pop))
- (goto interpret *code-pointer*))
- (define-opcode resume-interrupted-call-to-native-code
- (pop)
- (s48-pop-interrupt-state)
- (set! *val* (pop))
- (let ((protocol-skip (extract-fixnum (pop))))
- (goto really-call-native-code protocol-skip)))
- (define-opcode resume-native-poll
- (pop) ; frame size
- (s48-pop-interrupt-state)
- (let* ((return-address (pop))
- (template (pop)))
- (set! *val* (pop))
- (goto post-native-dispatch (s48-jump-native return-address template))))
- ; Do nothing much until something happens. To avoid race conditions this
- ; opcode is called with all interrupts disabled, so it has to return if
- ; any interrupt occurs, even a disabled one.
- (define-primitive wait (fixnum-> boolean->)
- (lambda (max-wait minutes?)
- (if (and (not (pending-interrupt?))
- (pending-interrupts-empty?))
- (wait-for-event max-wait minutes?))
- (goto return-unspecific 0)))
- ; The players:
- ; pending-interrupts-X A bit mask of pending interrupts
- ; *enabled-interrupts* A bit mask of enabled interrupts
- ; s48-*pending-interrupt?* True if either an event or interrupt is pending
- ; s48-*pending-events?* True if an event is pending
- ;
- ; When an asynchronous event occurs the OS sets S48-*PENDING-EVENTS?* and
- ; S48-*PENDING-INTERRUPT?* to true.
- ;
- ; When S48-*PENDING-EVENTS?* is true the VM calls (CURRENT-EVENTS) to get the
- ; pending events.
- ;
- ; The goals of all this mucking about are:
- ; - no race conditions
- ; - the VM operates synchronously; only the OS is asynchronous
- ; - polling only requires testing S48-*PENDING-INTERRUPT?*
- (define s48-*pending-events?* #f)
- ; Called asynchronously by the OS
- (define (s48-note-event)
- (set! s48-*pending-events?* #t) ; order required by non-atomicity
- (set-interrupt-flag!))
- ; Called when the interrupt flag is set, so either an event or interrupt is
- ; waiting (or both). We process any events and then see if is an interrupt.
- (define (pending-interrupt?)
- (if s48-*pending-events?*
- (begin
- (set! s48-*pending-events?* #f)
- (process-events)))
- (real-pending-interrupt?))
- ; Check for a pending interrupt, clearing the interrupt flag if there is
- ; none. This and S48-NOTE-EVENT cooperate to avoid clearing the interrupt
- ; flag while an event is pending.
- (define (real-pending-interrupt?)
- (cond ((= 0 (bitwise-and (pending-interrupts-mask)
- *enabled-interrupts*))
- (clear-interrupt-flag!)
- (if s48-*pending-events?*
- (set-interrupt-flag!))
- #f)
- (else
- #t)))
- (define (update-pending-interrupts)
- (if (real-pending-interrupt?)
- (set-interrupt-flag!)))
- ; Add INTERRUPT to the set of pending interrupts, then check to see if it
- ; is currently pending.
- (define (note-interrupt! interrupt)
- (pending-interrupts-add! (interrupt-bit interrupt))
- (update-pending-interrupts))
- ; Remove INTERRUPT from the set of pending interrupts, then recheck for pending
- ; interrupts; INTERRUPT may have been the only one.
- (define (clear-interrupt! interrupt)
- (pending-interrupts-remove! (interrupt-bit interrupt))
- (update-pending-interrupts))
- ; Install a new set of enabled interrupts. As usual we have to recheck for
- ; enabled interrupts.
- (define (set-enabled-interrupts! enabled)
- (set! *enabled-interrupts* enabled)
- (update-pending-interrupts))
- ; Disable all interrupts.
- (define (disable-interrupts!)
- (set! s48-*pending-interrupt?* #f)
- (set! *enabled-interrupts* 0))
- ; Enable all interrupts.
- (define (enable-interrupts!)
- (set-enabled-interrupts! -1))
- ; We don't need to mess with S48-*PENDING-INTERRUPT?* because all interrupts
- ; are about to be disabled.
- (define (get-highest-priority-interrupt!)
- (let ((n (bitwise-and (pending-interrupts-mask) *enabled-interrupts*)))
- (let loop ((i 0) (m 1))
- (cond ((= 0 (bitwise-and n m))
- (loop (+ i 1) (* m 2)))
- (else
- (pending-interrupts-remove! m)
- i)))))
- ; Process any pending OS events. PROCESS-EVENT returns a mask of any interrupts
- ; that have just occured.
- (define (process-events)
- (let loop ()
- (receive (type channel status)
- (get-next-event)
- (pending-interrupts-add! (process-event type channel status))
- (if (not (eq? type (enum events no-event)))
- (loop)))))
- ; Do whatever processing the event requires.
- (define (process-event event id status)
- (cond ((eq? event (enum events alarm-event))
- ;; Save the interrupted template for use by profilers.
- ;; Except that we have no more templates and no more profiler.
- ;(if (false? *interrupted-template*)
- ; (set! *interrupted-template* *template*))
- (interrupt-bit (enum interrupt alarm)))
- ((eq? event (enum events keyboard-interrupt-event))
- (interrupt-bit (enum interrupt keyboard)))
- ((eq? event (enum events io-completion-event))
- (enqueue-channel! id status false)
- (interrupt-bit (enum interrupt i/o-completion)))
- ((eq? event (enum events io-error-event))
- (enqueue-channel! id status true)
- (interrupt-bit (enum interrupt i/o-completion)))
- ((eq? event (enum events os-signal-event))
- (interrupt-bit (enum interrupt os-signal)))
- ((eq? event (enum events external-event))
- (interrupt-bit (enum interrupt external-event)))
- ((eq? event (enum events no-event))
- 0)
- ((eq? event (enum events error-event))
- (error-message "OS error while getting event")
- (error-message (error-string status))
- 0)
- (else
- (error-message "unknown type of event")
- 0)))
- ; Return a bitmask for INTERRUPT.
- (define (interrupt-bit interrupt)
- (shift-left 1 interrupt))
|