wait-any should run now. no tests about race-conditions and the code is very ugly at the moment. I wonder if this ever will change

This commit is contained in:
marting 1999-10-18 23:32:01 +00:00
parent 30b32c7ff6
commit 55b3db0c72
1 changed files with 197 additions and 49 deletions

View File

@ -19,11 +19,24 @@
(zombie #t) ; Misnomer. Whether or not the process has
; (not) been waited on.
;; Make proc objects print like #{proc 2318}.
((disclose p) (list "proc" (proc:pid p))))
((disclose p) (list "proc" (proc:pid p) (proc:finished? p))))
;; Unfortunately there is no way to specify the name of the constructor-
;; function in Olins define-record macro, so I had to do this...
(define (make-procobj pid)
(let ((procobj (make-proc pid)))
(add-finalizer! procobj procobj-finalizer)
procobj))
;; Weak pointer tables. Much more efficient than populations.
;; Maps pids to processes. Unexited processes are strong pointers, exited
;; procs are weak pointers (to allow gc'ing).
;;
;; JMG: whyever unexited processes were strong pointer, this won't work
;; with (autoreap-policy 'late), since then gc waits for the strong pointer
;; until it wait(2)s and the strong pointer waits for wait(2) which is
;; nothing but a deadlock
(define process-table (make-integer-table))
@ -31,13 +44,13 @@
(let ((r (table-ref t n)))
(if (weak-pointer? r)
(weak-pointer-ref r)
r)))
(error "there was a non-weak-pointer" r))))
(define (weak-table-set! t n s)
(table-set! t n (make-weak-pointer s)))
(define (weaken-table-ref! t n)
(weak-table-set! t n (weak-table-ref t n)))
;(define (weaken-table-ref! t n)
; (weak-table-set! t n (weak-table-ref t n)))
(define (maybe-pid->proc pid)
(weak-table-ref process-table pid))
@ -47,7 +60,7 @@
(or (maybe-pid->proc pid)
(case probe?
((#f) (error "Pid has no corresponding process object" pid))
((create) (let ((p (make-proc pid))) ; Install a new one.
((create) (let ((p (make-procobj pid))) ; Install a new one.
;; Weak because we don't know what's up with this thing.
(weak-table-set! process-table pid p)
p))
@ -102,20 +115,85 @@
(let ((new-policy (car maybe-policy)))
(cond ((pair? (cdr maybe-policy))
(error "Too many args to autoreap-policy" maybe-policy))
((not (memq new-policy '(early #f)))
((not (memq new-policy '(early late #f)))
(error "Illegal autoreap policy." new-policy))
(else (set! *autoreap-policy* new-policy)
(if (eq? new-policy 'early)
(set! procobj-handler
(lambda (enabled-ints) (reap-zombies))))))))
(cond ((eq? new-policy 'early)
(sigchld-setter early-sigchld-handler)
(post/gc-setter (lambda a (reap-need-reaping))))
((eq? new-policy 'late)
(sigchld-setter late-sigchld-handler)
(post/gc-setter (lambda a (reap-need-reaping))))
(else
(sigchld-setter noauto-sigchld-handler)
(post/gc-setter
(lambda (enabled-ints)
(display "won't reap on gc since #f")))))))))
old-policy))
;;; New (scsh 0.6)
(define sigchld-setter
(lambda a (error "procobj setter was not defined")))
(define (post/gc-setter handler)
(set! post/gc-setter
(low-interrupt-register interrupt/post-gc
handler)))
;;; New (scsh 0.6)
;;; we register the post/gc-handler not until the first police change
(define (install-autoreaping)
(set! procobj-handler
(lambda (enabled-ints)
(reap-zombies))))
(let ((setter (low-interrupt-register interrupt/chld early-sigchld-handler)))
(set! *autoreap-policy* 'early)
(set! sigchld-setter setter)))
;;; This list contains pids whose proc-obj were gc'd before they died
;;; We try to reap them after every gc and maybe on every SIGCHLD
(define need-reaping '())
(define (need-reaping-add! pid)
(set! need-reaping (cons pid need-reaping)))
(define (need-reaping-remove! pid)
(set! need-reaping (del pid need-reaping)))
(define (reap-need-reaping)
(set! need-reaping (filter (lambda (pid) (not (reap-pid pid))) need-reaping)))
;;; reap this special pid
;;; return status or #f
(define (reap-pid pid)
(let ((status (really-wait pid wait/poll)))
(if status
(dead_by_reap pid status))
status))
;;; Handler for SIGCHLD according policy
(define (late-sigchld-handler enabled-ints)
(display "late sigchld handler"))
(define (early-sigchld-handler enabled-ints)
(reap-zombies))
(define (noauto-sigchld-handler enabled-ints)
(display "won't on chld reap since #f")
#f)
;;; Finalizer for procobjs
;;;
(define (procobj-finalizer procobj)
(display "procobj finalizer called")
(if (not (proc:finished? procobj))
(need-reaping-add! (proc:pid procobj))
(display "but was already finished")))
;;; (reap-zombies) => bool
;;; Move any zombies from the kernel process table into scsh.
@ -125,21 +203,21 @@
(display "reap-zombies was called" (current-error-port))
(newline)
(let lp ()
(receive (pid status) (%wait-any (bitwise-ior wait/poll wait/stopped-children))
(receive (pid status) (%wait-any (bitwise-ior wait/poll
wait/stopped-children))
(if pid
(begin (add-reaped-proc! pid status)
; (format (current-error-port)
; "Reaping ~d[~d]~%" pid status)
(begin (dead_by_reap pid status)
(format (current-error-port)
"Reaping ~d[~d]~%" pid status)
(lp))
status))))
(define (new-child-proc pid)
(let ((proc (make-proc pid)))
(table-set! process-table pid proc)
proc))
(define (mark-proc-exited proc)
(weaken-table-ref! process-table (proc:pid proc)))
(define (new-child-proc pid)
(let ((proc (make-procobj pid)))
(weak-table-set! process-table pid proc)
proc))
;;; (WAIT proc/pid [flags])
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -157,17 +235,53 @@
;;; is set) and wait/poll is set, return #f.
;;; (I'm working on the flags -df)
;;; JMG: We have to be careful about wait/poll and autoreap-policy:
;;; If it was 'late to anytime, we may missed the exit of pid/proc
;;; So we cannot just block and hope reap-zombies will give us the status
;;; TODO: add a jetzt-wart-i lock
(define (wait pid/proc . maybe-flags)
;;; I'm really tired of opening everything in scsh-level-0
(let ((wait-interrupt (structure-ref events wait-interrupt))
(most-recent-event (structure-ref events most-recent-event)))
(let* ((flags (:optional maybe-flags 0))
(proc (->proc pid/proc))
(win (lambda (status)
(mark-proc-waited! proc) ; Not eligible for a WAIT-ANY
(dead_by_wait proc status)
status)))
(if (or (zero? (bitwise-and flags wait/poll))
(proc:finished? proc))
(win (placeholder-value (proc:status proc)))
(proc:finished? proc))))
(cond ((proc:finished? proc)
(win (placeholder-value (proc:status proc))))
((zero? (bitwise-and flags wait/poll))
(let lp () ; we have to block and hence use the event system
(wait-interrupt interrupt/chld (most-recent-event))
(cond ((wait proc (bitwise-ior flags wait/poll)) => win)
(else (lp)))))
((eq? wait/poll (bitwise-and flags wait/poll))
(cond ((really-wait (proc:pid proc) flags) => win)
(else #f)))))))
;;; This one is used, to wait on a positive pid
;;; We NEVER do a blocking wait syscall
(define (really-wait pid flags)
(if (zero? (bitwise-and flags wait/poll))
(error "really-wait without wait/poll"))
(if (< pid 1)
(error "really-wait on nonpos pid" pid))
(receive (err return_pid status) (%wait-pid/errno pid flags)
(cond ((not err)
(cond ((zero? return_pid) #f) ; failed wait/poll
((= pid return_pid) status) ; made it
(else (error "mismatch in really-wait"
return_pid pid))))
((= err errno/intr)
(really-wait pid flags))
(else (errno-error err %wait-pid pid flags)))))
;;; Another way to do it:
;;; Every time we reap a process, we pop out of our SIGCHLD
;;; block so that we can service an interrupt if the system
@ -185,12 +299,32 @@
;Generally correct idea, tho
(define (cache-wait-status proc status)
(cond ((and (integer? status)
(not (status:stop-sig status))) ; He's dead, Jim.
(placeholder-set! (proc:status proc) status) ; Cache exit status.
(mark-proc-exited proc))) ; We're now gc'able.
status)
;;; TODO: These 2 have to be renamed !!!
;;; All you have to do, if pid was reaped
;;; proc_obj is maybe no longer alive
(define (dead_by_reap pid status)
(cond ((maybe-pid->proc pid) =>
(lambda (proc)
(obituary proc status)
(push-reaped-proc proc)))
(else (warn "reaped pid had no procobj" pid))))
;;; All you have to do, if a wait on proc was successful
(define (dead_by_wait proc status)
(obituary proc status)
(mark-proc-waited! proc))
;;; we know from somewhere that proc is dead
(define (obituary proc status)
(if (not (proc? proc))
(error "proc was not a proc" proc))
(need-reaping-remove! (proc:pid proc)) ; in case it started during 'late
(placeholder-set! (proc:status proc) status)
(set-proc:finished? proc #t))
;;; (wait-any [flags]) => [proc status]
@ -201,25 +335,46 @@
(define (wait-any . maybe-flags)
(let ((flags (:optional maybe-flags 0)))
(if (zero? (bitwise-and flags wait/poll))
(let ((win (get-reaped-proc!)))
(values win (placeholder-value (proc:status win))))
(begin
(receive (pid status)
;;; before we maybe block via placeholder-value
;;; do a really-wait-any for the ones, missed by 'late
(really-wait-any (bitwise-ior flags wait/poll))
(if (not pid)
(let ((win (get-reaped-proc!)))
(values win (placeholder-value (proc:status win))))
(values pid status))))
;The rest of this is quite crude and can be safely ignored. -df
;;; JMG: wait-any is crude and so it's implementation
;;; It got even worse, now that we have this fu*$#%g 'late
(if (maybe-obtain-lock reaped-proc-pop-lock)
(if (eq? reaped-proc-head reaped-proc-tail)
;;; due to 'late we cannot be sure, that they all have been
;;; reaped
(begin
(release-lock reaped-proc-pop-lock)
(values #f #f))
(really-wait-any flags))
(let* ((retnode (placeholder-value reaped-proc-head))
(retval (weak-pointer-ref (reaped-proc:proc retnode))))
(set! reaped-proc-head (reaped-proc:next retnode))
(release-lock reaped-proc-pop-lock)
(if retval
(values retval (placeholder-value (proc:status retval)))
(values #f #f))
(values #f #f)))))))
(values #f #f))))
(values #f #f)))))
(define (really-wait-any flags)
(if (zero? (bitwise-and flags wait/poll))
(error "real-wait-any without wait/poll" flags))
(receive (pid status) (%wait-any flags)
(if pid
(let ((proc (new-child-proc pid)))
(dead_by_wait proc status)
(values proc status))
(values #f #f))))
;;; (wait-process-group [proc-group flags]) => [proc status]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; [#f #f] => non-blocking, none ready.
@ -239,7 +394,7 @@
(receive (pid status) (%wait-process-group proc-group flags)
(if pid
(let ((proc (pid->proc pid)))
(cache-wait-status proc status)
(dead_by_wait proc status)
(values proc status))
(values pid status)))))) ; pid = #f -- Empty poll.
@ -336,14 +491,7 @@
(release-lock reaped-proc-pop-lock)
(weak-pointer-ref (reaped-proc:proc pop-me))))
(define (add-reaped-proc! pid status)
(cond ((maybe-pid->proc pid) =>
(lambda (proc)
(placeholder-set! (proc:status proc) status)
(set-proc:finished? proc #t)
(push-reaped-proc proc)
(mark-proc-exited proc))
(else (warn "Exiting child pid has no proc object." pid status)))))
;;; Pop one off the list.
(define (get-reaped-proc!)