;;; Copyright (c) 1993 by Olin Shivers. ;;; Signal handler system ;;; The principal trickiness here is that we have to interface to Unix signals ;;; *through* an intermediate interface, the S48 vm's idea of interrupts. ;;; So there is a difference between delivering a signal to the underlying ;;; Unix process and delivering it to the program that runs on the VM. ;;; ;;; One effect is that we have two separate codes for the same thing -- the ;;; Unix signal code, and the S48 interrupt value. E.g., SIGNAL/TSTP and ;;; INTERRUPT/TSTP. ;;; These system calls can return EINTR or restart. In order for the S48 vm's ;;; interrupt system to detect a signal and invoke the handler, they *must* ;;; return EINTR, and this must cause a return from C to Scheme. ;;; ;;; open close dup2 accept connect ;;; read recv recvfrom recvmsg ;;; write send sendto sendmsg ;;; select ;;; wait ;;; fcntl* ioctl ;;; sigsuspend ;;; HP-UX, but I don't use: poll lockf msem_lock msgsnd msgrcv semop ;;; ;;; * Only during a F_SETLKW ;;; ;;; From rts/interrupt.scm (package interrupts, interface interrupts-interface) ;;; WITH-INTERRUPTS INTERRUPT-HANDLERS SET-ENABLED-INTERRUPTS ! ;;; ENABLED-INTERRUPTS ;;; Must define WITH-INTERRUPTS* and WITH-INTERRUPTS. (foreign-init-name "sighandlers") (foreign-source "extern int errno;" "" "/* Make sure foreign-function stubs interface to the C funs correctly: */" "#include \"sighandlers1.h\"" "" "") ;;; Map a Unix async signal to its S48 interrupt value. ;;; -1 => Not defined. (define-stubless-foreign %signal->interrupt (sig) "sig2interrupt") (define (signal->interrupt sig) (let ((int (%signal->interrupt sig))) (if (>= int 0) int (error "Unix signal has no Scheme 48 interrupt." sig)))) (define (interrupt-set . interrupts) (let lp ((ints interrupts) (ans 0)) (if (pair? ints) (lp (cdr ints) (bitwise-ior ans (arithmetic-shift 1 (car ints) ))) ans))) (define (interrupt-enabled? int mask) (not (zero? (bitwise-and (arithmetic-shift 1 int) mask)))) (define (interrupt-enable int mask) (bitwise-ior (arithmetic-shift 1 int) mask)) (define *enabled-interrupts* (let lp ((i 0) (mask 0)) (if (= i number-of-interrupts) mask (lp (+ i 1) (interrupt-enable i mask))))) (define (enabled-interrupts) *enabled-interrupts*) ;;; I'm trying to be consistent about the ! suffix -- I don't use it ;;; when frobbing process state. This is not a great rule; perhaps I ;;; should change it. ;;; ;;; I think you should... (define (set-enabled-interrupts new-enabled-interrupts) (do ((int 0 (+ int 1))) ((= int number-of-interrupts) new-enabled-interrupts) (let ((old-state (interrupt-enabled? int *enabled-interrupts*)) (new-state (interrupt-enabled? int new-enabled-interrupts))) (cond ((and old-state (not new-state)) (vector-set! blockade-vector int (block-interrupt int))) ((and (not old-state) new-state) (let ((blockade (vector-ref blockade-vector int))) (if (not blockade) (error "there was no blockade" int)) (unblock-interrupt blockade) (vector-set! blockade-vector int #f))) (else 'unchanged)))) (set! *enabled-interrupts* new-enabled-interrupts)) ;;; Enableing/Disableing = Unblocking/Blocking ;;; ;;; issues: ;;; + prevent delivery of the interrupt => install fake handler in ;;; low-interrupt ;;; + support setting of handler during blocking => install fake ;;; set-proc in interrupt-handler-vector ;;; + record if an interrupt occures while interupt blocked => pending? ;;; + restore everything after interrupt unublocked => reinstall handler ;;; in low-interrupt, set-proc in interrupt-handler-vector ;;; + if pending? interrupt: call handler ;(define-record-type blockade :blockade ; (really-make-blockade interrupt-vector-cell pending? low-int-set!) ; blockade? ; (interrupt-vector-cell blockade:interrupt-vector-cell) ; (pending? blockade:pending? set-blockade:pending?) ; (low-int-set! blockade:low-int-set!)) (define-record blockade interrupt-vector-cell low-int-set! ; proc to set interrupt in low-interrupt (pending? #f)) (define blockade-vector (make-vector number-of-interrupts #f)) ;;; do nothing in low-interrupt, the new handler will be recorded in the ;;; interrupt-handler-vector however (define (fake-set-interrupt blockade) (lambda (new-handler) #f)) ;;; to be installed in low-interrupt (define (fake-handler blockade) (lambda a (if (not (blockade:pending? blockade)) (set-blockade:pending? blockade a)))) ;;; generate blockade and install fake handler and set-proc (define (block-interrupt int) (let* ((handler-setter-cell (vector-ref *interrupt-handlers-vector* int)) (low-int-set! (cdr handler-setter-cell)) (blockade (make-blockade handler-setter-cell low-int-set!))) ; fade out the low-interupt-set (set-cdr! handler-setter-cell (fake-set-interrupt blockade)) ; set the fake handler in low-interupt: ((blockade:low-int-set! blockade) (fake-handler blockade)) blockade)) (define (unblock-interrupt blockade) (let ((handler (car (blockade:interrupt-vector-cell blockade)))) ; install the handler that resides in the vector (let ((low-int-set! (blockade:low-int-set! blockade))) (low-int-set! handler) ; reinstall the low-interrupt-setter (set-cdr! (blockade:interrupt-vector-cell blockade) low-int-set!) (if (blockade:pending? blockade) (apply handler (blockade:pending? blockade)))))) (define-simple-syntax (with-enabled-interrupts interrupt-set body ...) (begin (with-enabled-interrupts* interrupt-set (lambda () body ...)))) (define (with-enabled-interrupts* interrupt-set thunk) (let ((before *enabled-interrupts*)) (set-enabled-interrupts interrupt-set) (let ((return (thunk))) (set-enabled-interrupts before) return))) ; Fakes vm vector ;;; car is the actual handler, cdr is a proc to set handler in ;;; low-interrupt system (define *interrupt-handlers-vector* (make-vector number-of-interrupts (cons #f #f))) (define (interrupt-handlers-vector) *interrupt-handlers-vector*) (define (interrupt-handler-ref int) (if (or (< int 0) (>= int number-of-interrupts)) (error "ill signum in interrupt-handler-ref" int) (car (vector-ref *interrupt-handlers-vector* int)))) ;;; the handler is not interested in the enabled interupts of the vm ;;; but in those managed here (define (make-handler handler) (lambda (enabled-low) (handler (enabled-interrupts)))) (define (set-interrupt-handler! int handler) (if (or (< int 0) (>= int number-of-interrupts)) (error "ill signum in set-interrupt-handler!" int) (let ((handler-setter (vector-ref *interrupt-handlers-vector* int)) (handler-enabled-here (make-handler handler))) (if (not (cdr handler-setter)) ; not yet registered? (let ((setter (low-interrupt-register int handler-enabled-here))) (vector-set! *interrupt-handlers-vector* int (cons handler setter))) (begin ((cdr handler-setter) handler-enabled-here) ; set it with setter (set-car! (vector-ref *interrupt-handlers-vector* int) handler)))))) ;;; Get/Set signal handlers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; When you set a signal's handler to "default," if the default for that ;;; signal is something other than "ignore," we actually install this guy. ;;; When he is called by the S48 interrupt system, he'll magically make ;;; the default action happen (by calling C code that *really* sets the ;;; handler to SIGDFL, and then re-sending the signal). This basically ;;; terminates the process, since if the default isn't "ignore," it's always ;;; "terminate" of some kind. Doing it this way means the exit code given ;;; to our waiting parent proc correctly reflects how we died, and also ;;; makes the core dump happen if it should. Details, details. (define-stubless-foreign %do-default-sigaction (signal) "do_default_sigaction") (define default-int-handler-vec ;; Non-Unix-signal interrupts just get their default values from ;; the current value of I-H. (let ((v (make-vector 32))) (do ((sig 31 (- sig 1))) ; For each Unix signal ((< sig 0)) ; make & install a default (let ((i (%signal->interrupt sig))) ; signal handler. (if (>= i 0) ; Don't mess with non-signal interrupts. (vector-set! v i (if (memv sig signals-ignored-by-default) (lambda (enabled-interrupts) #f) (lambda (enabled-interrupts) (%do-default-sigaction sig))))))) v)) ;;; HANDLER is #f (ignore), #t (default), or a procedure taking an integer ;;; argument. The interrupt is delivered to a procedure by (1) setting the ;;; ENABLED-INTERRUPTS register to 0 (i.e., blocking all interrupts), and (2) ;;; applying the procedure to the previous value of the ENABLED-INTERRUPTS ;;; register. If the procedure returns normally, the ENABLED-INTERRUPTS ;;; register will be restored to its previous value. ;;; This handler does nothing -- used when the handler is #f. (define (noop-sig-handler enabled-interrupts) #f) (define (set-interrupt-handler int handler) (let ((ohandler (interrupt-handler int))) (set-interrupt-handler! int (case handler ((#t) (vector-ref default-int-handler-vec int)) ((#f) noop-sig-handler) (else handler))) ohandler)) ; (cond ((and (not handler) ohandler ; Toggling from something ; (int->signal int)) => ; to ignored. ; (lambda (sig) ; (%set-unix-signal-handler sig 0))) ; ((and handler (not ohandler) ; Toggling from ignored ; (int->signal int)) => ; to something. ; (lambda (sig) ; (%set-unix-signal-handler sig 2)))) ; ohandler)) (define (interrupt-handler int) (let ((handler (interrupt-handler-ref int))) (cond ((eq? handler (vector-ref default-int-handler-vec int)) #t) ((eq? handler noop-sig-handler) #f) (else handler)))) (define (%install-scsh-handlers interactive?) (do ((int 0 (+ int 1))) ((= int number-of-interrupts)) (set-interrupt-handler int (lambda a #f))) (do ((sig 32 (- sig 1))) ((< sig 0)) (let ((i (%signal->interrupt sig))) (if (not (or (= i -1) ; (= sig signal/int) ; Leave ^c and (= sig signal/alrm))) ; alarm handlers alone. (set-interrupt-handler i (vector-ref default-int-handler-vec i))))) (let ((scheduler-initial-thread (current-thread))) (if (not (eq? (thread-name scheduler-initial-thread) 'scheduler-initial-thread)) (error "sighandler did not find scheduler-initial-thread, but" scheduler-initial-thread)) ;; Note: this will prevent any other system to work, since it pushes ;; a new command level ! (if interactive? (set-interrupt-handler interrupt/keyboard (lambda stuff ((structure-ref threads-internal schedule-event) scheduler-initial-thread (enum (structure-ref threads-internal event-type) interrupt) (enum interrupt keyboard))))))) ;;; I am ashamed to say the 33 below is completely bogus. ;;; What we want is a value that is 1 + max interrupt value. (define int->sig-vec (let ((v (make-vector 33 #f))) (do ((sig 32 (- sig 1))) ((< sig 0)) (let ((i (%signal->interrupt sig))) (if (not (= i -1)) (vector-set! v i sig)))) v)) (define (int->signal i) (and (<= 0 i 32) (vector-ref int->sig-vec i)))