; Copyright (c) 1999-2001 by Martin Gasbichler. See file COPYING.

;;; Functional event system.
;;; System by Olin Shivers, implementation by Martin Gasbichler

(define-record-type sigevent :sigevent
  (really-make-sigevent type next)
  sigevent?
  (type sigevent-type set-sigevent-type!)
  (next sigevent-next set-sigevent-next!))

(define (make-sigevent type)
  (really-make-sigevent type #f))

(define empty-sigevent (make-sigevent #f))

(define *most-recent-sigevent* empty-sigevent)

(define (most-recent-sigevent) *most-recent-sigevent*)

(define sigevent-thread-queue #f)

;Wait for an sigevent of a certain type.
(define (rts-next-sigevent pre-sigevent set type-in-set?)
  (with-interrupts-inhibited
   (lambda ()
     (let lp ((pre-sigevent pre-sigevent))
       (let ((sigevent (sigevent-next pre-sigevent)))
	 (if sigevent
	     (if (type-in-set? (sigevent-type sigevent) set)
		 sigevent
		 (lp sigevent))
	     (begin (enqueue-thread! sigevent-thread-queue (current-thread))
		    (block)
		    (lp pre-sigevent))))))))

; same as above, but don't block 
(define (rts-next-sigevent/no-wait pre-sigevent set type-in-set?)
  (let ((sigevent (sigevent-next pre-sigevent)))
    (if sigevent
	(if (type-in-set? (sigevent-type sigevent) set)
	    sigevent
	    (rts-next-sigevent/no-wait (sigevent-next sigevent) 
				       set 
				       type-in-set?))
	#f)))


;Called when the interrupt actually happened.
;;; TODO w-i-i is problaly not necessary since they're off already
(define (register-interrupt type)
  (let ((waiters (with-interrupts-inhibited
		  (lambda ()
		    (set-sigevent-next! *most-recent-sigevent* (make-sigevent type))
		    (set! *most-recent-sigevent* (sigevent-next *most-recent-sigevent*))
		    (do ((waiters '() (cons (dequeue-thread! sigevent-thread-queue)
					    waiters)))
			((thread-queue-empty? sigevent-thread-queue)
			 waiters))))))
    (for-each make-ready waiters)))

;;; has to be called with interrupts disabled
(define (waiting-for-sigevent?)
  (not (thread-queue-empty? sigevent-thread-queue)))
  
(define (initialize-sigevents!)
  (set! sigevent-thread-queue (make-thread-queue))
  (set-interrupt-handler! (enum interrupt os-signal) 
			  (lambda (type arg enabled-interrupts)
                            ; type is already set in the unix signal handler
			    (register-interrupt type)))
  (set-interrupt-handler! (enum interrupt keyboard) 
			  (lambda (enabled-interrupts)
			    (register-interrupt (enum interrupt keyboard))))
;  (call-after-gc! (lambda () (register-interrupt (enum interrupt post-gc))))
)
;;; the vm uses the timer for the scheduler
(define (schedule-timer-interrupt! msec)
  (spawn (lambda ()
	   (sleep msec)
	   (register-interrupt (enum interrupt alarm)))))