wingo pushed a commit to branch wip-whippet in repository guile. commit f436e550eac344552957e0f199cf5a0c2258e3e0 Author: Andy Wingo <wi...@pobox.com> AuthorDate: Mon Jun 16 09:22:48 2025 +0200
Pipes use finalizers instead of guardians * module/ice-9/popen.scm (reap-pipe, open-pipe*): Instead of pumping a guardian in the after-gc-hook, attach a finalizer. This will pump pipes in a thread instead of needing to do it ourselves. --- module/ice-9/popen.scm | 45 ++++++++++++++++++--------------------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/module/ice-9/popen.scm b/module/ice-9/popen.scm index 43b5d2f62..e3c143e7e 100644 --- a/module/ice-9/popen.scm +++ b/module/ice-9/popen.scm @@ -20,11 +20,11 @@ (define-module (ice-9 popen) #:use-module (ice-9 binary-ports) - #:use-module (ice-9 guardians) #:use-module (ice-9 threads) #:use-module (ice-9 weak-tables) #:use-module (srfi srfi-1) #:use-module (srfi srfi-9) + #:use-module (system finalizers) #:export (open-pipe* open-pipe close-pipe open-input-pipe open-output-pipe open-input-output-pipe pipeline)) @@ -76,10 +76,6 @@ rw-port) -;; a guardian to ensure the cleanup is done correctly when -;; an open pipe is gc'd or a close-port is used. -(define pipe-guardian (make-guardian)) - (define (pipe->fdes) (let ((p (pipe))) (cons (port->fdes (car p)) @@ -137,7 +133,7 @@ port to the process is created: it should be the value of ;; Guard the pipe-info instead of the port, so that we can still ;; call 'waitpid' even if 'close-port' is called (which clears ;; the port entry). - (pipe-guardian pipe-info) + (add-finalizer! pipe-info reap-pipe) (%set-port-property! port 'popen-pipe-info pipe-info) port)))) @@ -171,27 +167,22 @@ information on how to interpret this value." (set-pipe-info-pid! pipe-info #f) (close-process p pid)))) -(define (reap-pipes) - (let loop () - (let ((pipe-info (pipe-guardian))) - (when pipe-info - (let ((pid (pipe-info-pid pipe-info))) - ;; maybe 'close-pipe' was already called. - (when pid - ;; clean up without reporting errors. also avoids blocking - ;; the process: if the child isn't ready to be collected, - ;; puts it back into the guardian's live list so it can be - ;; tried again the next time the cleanup runs. - (catch 'system-error - (lambda () - (let ((pid/status (waitpid pid WNOHANG))) - (if (zero? (car pid/status)) - (pipe-guardian pipe-info) ; not ready for collection - (set-pipe-info-pid! pipe-info #f)))) - (lambda args #f)))) - (loop))))) - -(add-hook! after-gc-hook reap-pipes) +(define (reap-pipe pipe-info) + (let ((pid (pipe-info-pid pipe-info))) + ;; Maybe 'close-pipe' was already called. + (when pid + ;; If the process is has exited, we need to waitpid() on it, to + ;; allow the OS to do the final process teardown. If it hasn't + ;; exited yet, keep the pipe-info alive and re-add reap-pipe as a + ;; finalizer so we can check again after the next GC. + (catch 'system-error + (lambda () + (let ((pid/status (waitpid pid WNOHANG))) + (if (zero? (car pid/status)) + ;; not ready for collection + (add-finalizer! pipe-info reap-pipe) + (set-pipe-info-pid! pipe-info #f)))) + (lambda args #f))))) (define (open-input-pipe command) "Equivalent to @code{open-pipe} with mode @code{OPEN_READ}"