[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

bug#15228: [PATCH] Close output port of I/O pipes

From: Josep Portella Florit
Subject: bug#15228: [PATCH] Close output port of I/O pipes
Date: Sat, 31 Aug 2013 10:29:57 +0200

There is a missing feature for pipes created with mode OPEN_BOTH:

(use-modules (ice-9 popen))
(use-modules (rnrs io ports))

(let ((p (open-pipe "md5sum" OPEN_BOTH)))
  (put-string p "hello")
  (let ((x (get-string-all p)))
    (close-pipe p)

This code deadlocks in get-string-all because md5sum, like other
filters, keeps waiting for input until the pipe's output port is

The output port can't be closed without closing the input port too,
because an I/O pipe is a soft port that doesn't store the 2 ports
returned by open-process, but a thunk which closes both ports.

This is now possible with the new procedure close-pipe-output:

(let ((p (open-pipe "md5sum" OPEN_BOTH)))
  (put-string p "hello")
  (close-pipe-output p)
  (let ((x (get-string-all p)))
    (close-pipe p)
;; => "5d41402abc4b2a76b9719d911017c592  -\n"

The intention is to make a backwards compatible and minimal change
that makes it possible to write to and read from pipes for filters
like md5sum without temporary files.

Changes involved:

* module/ice-9/popen.scm: Define a weak hash-table for mapping I/O pipes to
  their output ports, change make-rw-port to use it, define the
  close-pipe-output procedure and export it.

* doc/ref/posix.texi: Add documentation for close-pipe-output.

On garbage collection the new hash-table is updated as expected:

scheme@(ice-9 popen)> rw/w-table
$3 = #<weak-key-hash-table 8b8a930 0/31>
scheme@(ice-9 popen)> (define p (open-pipe "md5sum" OPEN_BOTH))
scheme@(ice-9 popen)> rw/w-table
$4 = #<weak-key-hash-table 8b8a930 1/31>
scheme@(ice-9 popen)> (set! p #f)
scheme@(ice-9 popen)> (gc)
scheme@(ice-9 popen)> rw/w-table
$5 = #<weak-key-hash-table 8b8a930 0/31>

Maybe there is a better name for the new procedure.
 doc/ref/posix.texi     |    6 ++++++
 module/ice-9/popen.scm |   39 +++++++++++++++++++++++++++++----------
 2 files changed, 35 insertions(+), 10 deletions(-)

diff --git a/doc/ref/posix.texi b/doc/ref/posix.texi
index b3a6a04..f0c6ca1 100644
--- a/doc/ref/posix.texi
+++ b/doc/ref/posix.texi
@@ -2312,6 +2312,12 @@ terminate, and return the wait status code.  The status 
is as per
 @end deffn
address@hidden {Scheme Procedure} close-pipe-output port
+Close the output port of a pipe created by @code{open-pipe} with
+mode @code{OPEN_BOTH}, and leave the input port open.  Return `#t' if
+the port is closed successfully or `#f' if it was already closed.
address@hidden deffn
 @sp 1
 @code{waitpid WAIT_ANY} should not be used when pipes are open, since
 it can reap a pipe's child process, causing an error from a subsequent
diff --git a/module/ice-9/popen.scm b/module/ice-9/popen.scm
index 7d0549e..2b014c5 100644
--- a/module/ice-9/popen.scm
+++ b/module/ice-9/popen.scm
@@ -18,22 +18,32 @@
 (define-module (ice-9 popen)
-  :export (port/pid-table open-pipe* open-pipe close-pipe open-input-pipe
-          open-output-pipe open-input-output-pipe))
+  :export (port/pid-table open-pipe* open-pipe close-pipe close-pipe-output
+           open-input-pipe open-output-pipe open-input-output-pipe))
 (eval-when (load eval compile)
   (load-extension (string-append "libguile-" (effective-version))
+;; a weak hash-table to store the write port of read-write pipes
+;; just to be able to retrieve it in close-pipe-output.
+(define rw/w-table (make-weak-key-hash-table 31))
 (define (make-rw-port read-port write-port)
-  (make-soft-port
-   (vector
-    (lambda (c) (write-char c write-port))
-    (lambda (s) (display s write-port))
-    (lambda () (force-output write-port))
-    (lambda () (read-char read-port))
-    (lambda () (close-port read-port) (close-port write-port)))
-   "r+"))
+  (letrec ((port (make-soft-port
+                  (vector
+                   (lambda (c) (write-char c write-port))
+                   (lambda (s) (display s write-port))
+                   (lambda () (force-output write-port))
+                   (lambda () (read-char read-port))
+                   (lambda ()
+                     (hashq-remove! rw/w-table port)
+                     (close-port read-port)
+                     (or (port-closed? write-port)
+                         (close-port write-port))))
+                  "r+")))
+    (hashq-set! rw/w-table port write-port)
+    port))
 ;; a guardian to ensure the cleanup is done correctly when
 ;; an open pipe is gc'd or a close-port is used.
@@ -106,6 +116,15 @@ information on how to interpret this value."
         (error "close-pipe: pipe not in table"))
     (close-process (cons p pid))))
+(define (close-pipe-output pipe)
+  "Closes the output port of a pipe created by @code{open-pipe} with
+mode @code{OPEN_BOTH}, and leaves the input port open.  Returns `#t' if
+it successfully closes the port or `#f' if it was already closed."
+  (let ((port (hashq-ref rw/w-table pipe)))
+    (unless port
+      (error "close-pipe-output: pipe not in table"))
+    (close-port port)))
 (define reap-pipes
   (lambda ()
     (let loop ((p (pipe-guardian)))

reply via email to

[Prev in Thread] Current Thread [Next in Thread]