< 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 >