;;; 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.

;;; 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

(foreign-source
  "extern int errno;"
  ""
  "/* Make sure foreign-function stubs interface to the C funs correctly: */"
  "#include \"sighandlers1.h\""
  "" "")

;;; Blocking interrupts
;;; I think all of this code (and associated C code) has been obsoleted by
;;; the new system that uses S48's sigblocking machinery.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define (interrupt-set . interrupts)
  (let lp ((ints interrupts) (ans 0))
    (if (pair? ints)
	(lp (cdr ints) (bitwise-ior ans (arithmetic-shift 1 (- (car ints) 1))))
	ans)))

(define-simple-syntax (with-blocked-interrupts mask body ...)
  (with-blocked-interrupts* mask (lambda () body ...)))

(define (with-blocked-interrupts* mask thunk)
  (let ((old-mask #f))
    (dynamic-wind
        (lambda () (set! old-mask (set-blocked-interrupts! mask)))
	thunk
	(lambda () (set-blocked-interrupts! old-mask)))))

(define (set-blocked-interrupts! mask)
  (receive (hi-out lo-out)
      (%set-blocked-interrupts! (hi8 mask) (lo24 mask))
    (compose-8/24 hi-out lo-out)))
	       

(define (blocked-interrupts)
  (call-with-values %blocked-interrupts compose-8/24))

(define-foreign %set-blocked-interrupts! (set_procmask (fixnum hi)
						       (fixnum lo))
  fixnum  ; hi
  fixnum) ; lo

(define-foreign %blocked-interrupts (get_procmask)
  fixnum  ; hi
  fixnum) ; lo


;;; 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 (default-handler sig)
  (lambda (enabled-interrupts) (%do-default-sigaction sig)))

(define-foreign %do-default-sigaction (do_default_sigaction (fixnum signal))
  ignore)


;;; HANDLER is #f (ignore), #t (default), or an integer procedure.
;;; 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
;;; (i.e., it doesn't throw to a continuation), the ENABLED-INTERRUPTS
;;; register will be restored to its previous value.

;;; Should have extra args, MASK & FLAGS.
(define (set-signal-handler sig handler)
  (let ((handler (if (eq? handler #t)		; Hack the default handler.
		     (default-handler sig)
		     handler)))
    (receive (handler flags) 		; Should be (handler mask flags).
	     (%set-signal-handler! sig handler 0)
      handler)))

(define (signal-handler sig)
  (receive (handler flags) (%signal-handler sig)
    handler))

(define (%signal-handler sig)
  (receive (err handler flags) (%%signal-handler sig)
    (if err (errno-error err %signal-handler sig)
	(values handler flags))))

;;; (%set-signal-handler! sig handler [mask flags]) -> [handler mask flags]
;;; Except no MASK for now.

(define (%set-signal-handler! sig handler . args)
  (let-optionals args ((flags 0))
    (receive (err handler flags)
  	     (%%set-signal-handler! sig handler flags)
      (if err
	  (errno-error err %set-signal-handler! sig handler flags)
	  (values handler flags)))))

(define-foreign %%set-signal-handler! (set_sig_handler (fixnum signal)
						       (desc handler)
						       (fixnum flags))
  desc		; #f or errno
  desc		; handler
  fixnum)	; flags

(define-foreign %%signal-handler (get_sig_handler (fixnum signal))
  desc		; #f or errno
  desc		; handler
  fixnum)	; flags

(define-foreign %%install-new-handler-vec
                (install_new_handler_vector (vector-desc vec))
		ignore)