< prev index next >

src/java.base/windows/classes/java/lang/ProcessImpl.java

Print this page

        

@@ -23,21 +23,24 @@
  * questions.
  */
 
 package java.lang;
 
+import sun.nio.ch.SelChImpl;
+
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
+import java.io.Closeable;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.lang.Override;
 import java.lang.ProcessBuilder.Redirect;
+import java.nio.channels.Pipe;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.ArrayList;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.TimeUnit;

@@ -100,59 +103,110 @@
 
         FileInputStream  f0 = null;
         FileOutputStream f1 = null;
         FileOutputStream f2 = null;
 
+        Pipe p0 = null;
+        Pipe p1 = null;
+        Pipe p2 = null;
+
         try {
             long[] stdHandles;
             if (redirects == null) {
                 stdHandles = new long[] { -1L, -1L, -1L };
             } else {
                 stdHandles = new long[3];
 
-                if (redirects[0] == Redirect.PIPE)
+                if (redirects[0] == Redirect.PIPE) {
                     stdHandles[0] = -1L;
-                else if (redirects[0] == Redirect.INHERIT)
+                } else if (redirects[0] == Redirect.PIPE_CHANNEL) {
+                    p0 = Pipe.open();
+                    if (!(p0.source() instanceof SelChImpl)) {
+                        throw new UnsupportedOperationException("Unsupported SelectorProvider");
+                    }
+                    stdHandles[0] = ((SelChImpl) p0.source()).getFDVal();
+                } else if (redirects[0] == Redirect.INHERIT) {
                     stdHandles[0] = fdAccess.getHandle(FileDescriptor.in);
-                else {
+                } else {
                     f0 = new FileInputStream(redirects[0].file());
                     stdHandles[0] = fdAccess.getHandle(f0.getFD());
                 }
 
-                if (redirects[1] == Redirect.PIPE)
+                if (redirects[1] == Redirect.PIPE) {
                     stdHandles[1] = -1L;
-                else if (redirects[1] == Redirect.INHERIT)
+                } else if (redirects[1] == Redirect.PIPE_CHANNEL) {
+                    p1 = Pipe.open();
+                    if (!(p1.sink() instanceof SelChImpl)) {
+                        throw new UnsupportedOperationException("Unsupported SelectorProvider");
+                    }
+                    stdHandles[1] = ((SelChImpl) p1.sink()).getFDVal();
+                } else if (redirects[1] == Redirect.INHERIT) {
                     stdHandles[1] = fdAccess.getHandle(FileDescriptor.out);
-                else {
+                } else {
                     f1 = newFileOutputStream(redirects[1].file(),
                                              redirects[1].append());
                     stdHandles[1] = fdAccess.getHandle(f1.getFD());
                 }
 
-                if (redirects[2] == Redirect.PIPE)
+                if (redirects[2] == Redirect.PIPE) {
                     stdHandles[2] = -1L;
-                else if (redirects[2] == Redirect.INHERIT)
+                } else if (redirects[2] == Redirect.PIPE_CHANNEL) {
+                    if (redirectErrorStream) {
+                        stdHandles[2] = -1;
+                    } else {
+                        p2 = Pipe.open();
+                        if (!(p2.sink() instanceof SelChImpl)) {
+                            throw new UnsupportedOperationException("Unsupported SelectorProvider");
+                        }
+                        stdHandles[2] = ((SelChImpl) p2.sink()).getFDVal();
+                    }
+                } else if (redirects[2] == Redirect.INHERIT) {
                     stdHandles[2] = fdAccess.getHandle(FileDescriptor.err);
-                else {
+                } else {
                     f2 = newFileOutputStream(redirects[2].file(),
                                              redirects[2].append());
                     stdHandles[2] = fdAccess.getHandle(f2.getFD());
                 }
             }
 
             return new ProcessImpl(cmdarray, envblock, dir,
-                                   stdHandles, redirectErrorStream);
+                                   stdHandles, new Pipe[] { p0, p1, p2 }, redirectErrorStream);
+        } catch (Exception e) {
+            // close unused pipe ends on exception
+            if (p0 != null) close(p0.sink());
+            if (p1 != null) close(p1.source());
+            if (p2 != null) close(p2.source());
+            // close subprocess pipe ends on exception
+            if (p0 != null) close(p0.source());
+            if (p1 != null) close(p1.sink());
+            if (p2 != null) close(p2.sink());
+            // re-throw
+            throw e;
         } finally {
-            // In theory, close() can throw IOException
-            // (although it is rather unlikely to happen here)
-            try { if (f0 != null) f0.close(); }
-            finally {
-                try { if (f1 != null) f1.close(); }
-                finally { if (f2 != null) f2.close(); }
+            // close subprocess file handles
+            close(f0);
+            close(f1);
+            close(f2);
+            // don't close subprocess pipe ends here as
+            // closing them prevents subprocess from  reading / writing
+            // (this differs from UNIX where the pipe handles are dup-ed
+            // and we can close the original handles as soon as the subprocess
+            // is forked)...
+            // on Windows, subprocess pipe ends must be closed when
+            // subprocess exits (see the end of ProcessImpl constructor)
             }
         }
 
+    private static IOException close(Closeable closeable) {
+        if (closeable != null) {
+            try {
+                closeable.close();
+            } catch (IOException e) {
+                return e;
+            }
+        }
+        return null;
     }
 
     private static class LazyPattern {
         // Escape-support version:
         //    "(\")((?:\\\\\\1|.)+?)\\1|([^\\s\"]+)";

@@ -315,14 +369,19 @@
     private final ProcessHandle processHandle;
     private OutputStream stdin_stream;
     private InputStream stdout_stream;
     private InputStream stderr_stream;
 
+    private final Pipe.SinkChannel stdinChannel;
+    private final Pipe.SourceChannel stdoutChannel;
+    private final Pipe.SourceChannel stderrChannel;
+
     private ProcessImpl(String cmd[],
                         final String envblock,
                         final String path,
                         final long[] stdHandles,
+                        final Pipe[] pipes,
                         final boolean redirectErrorStream)
         throws IOException
     {
         String cmdstr;
         SecurityManager security = System.getSecurityManager();

@@ -421,10 +480,25 @@
                 fdAccess.setHandle(stderr_fd, stdHandles[2]);
                 stderr_stream = new FileInputStream(stderr_fd);
             }
 
             return null; }});
+
+        stdinChannel = (pipes[0] == null) ? null : pipes[0].sink();
+        stdoutChannel = (pipes[1] == null) ? null : pipes[1].source();
+        stderrChannel = (pipes[2] == null) ? null : pipes[2].source();
+
+        if (pipes[0] != null || pipes[1] != null || pipes[2] != null) {
+            ProcessHandleImpl.completion(processHandle.getPid(), false)
+                    .handle((exitStatus, unusedThrowable) -> {
+                        // close subprocess pipe ends when it exits
+                        if (pipes[0] != null) close(pipes[0].source());
+                        if (pipes[1] != null) close(pipes[1].sink());
+                        if (pipes[2] != null) close(pipes[2].sink());
+                        return null;
+                    });
+        }
     }
 
     public OutputStream getOutputStream() {
         return stdin_stream;
     }

@@ -435,10 +509,22 @@
 
     public InputStream getErrorStream() {
         return stderr_stream;
     }
 
+    public Pipe.SinkChannel getOutputChannel() {
+        return stdinChannel;
+    }
+
+    public Pipe.SourceChannel getInputChannel() {
+        return stdoutChannel;
+    }
+
+    public Pipe.SourceChannel getErrorChannel() {
+        return stderrChannel;
+    }
+
     protected void finalize() {
         closeHandle(handle);
     }
 
     private static final int STILL_ACTIVE = getStillActive();
< prev index next >