--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/java/lang/UNIXProcess.java.aix Tue Nov 26 16:40:31 2013 +0100
@@ -0,0 +1,419 @@
+/*
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+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.util.Arrays;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.security.AccessController;
+import static java.security.AccessController.doPrivileged;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+/**
+ * java.lang.Process subclass in the UNIX environment.
+ *
+ * @author Mario Wolczko and Ross Knippel.
+ * @author Konstantin Kladko (ported to Linux)
+ * @author Martin Buchholz
+ * @author Volker Simonis (ported to AIX)
+ */
+final class UNIXProcess extends Process {
+ private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
+ = sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
+
+ private final int pid;
+ private int exitcode;
+ private boolean hasExited;
+
+ private /* final */ OutputStream stdin;
+ private /* final */ InputStream stdout;
+ private /* final */ InputStream stderr;
+
+ private static enum LaunchMechanism {
+ FORK(1),
+ POSIX_SPAWN(2);
+
+ private int value;
+ LaunchMechanism(int x) {value = x;}
+ };
+
+ /* On AIX, the default is to spawn */
+ private static final LaunchMechanism launchMechanism;
+ private static byte[] helperpath;
+
+ private static byte[] toCString(String s) {
+ if (s == null)
+ return null;
+ byte[] bytes = s.getBytes();
+ byte[] result = new byte[bytes.length + 1];
+ System.arraycopy(bytes, 0,
+ result, 0,
+ bytes.length);
+ result[result.length-1] = (byte)0;
+ return result;
+ }
+
+ static {
+ launchMechanism = AccessController.doPrivileged(
+ new PrivilegedAction<LaunchMechanism>()
+ {
+ public LaunchMechanism run() {
+ String javahome = System.getProperty("java.home");
+ String osArch = System.getProperty("os.arch");
+
+ helperpath = toCString(javahome + "/lib/" + osArch + "/jspawnhelper");
+ String s = System.getProperty(
+ "jdk.lang.Process.launchMechanism", "posix_spawn");
+
+ try {
+ return LaunchMechanism.valueOf(s.toUpperCase());
+ } catch (IllegalArgumentException e) {
+ throw new Error(s + " is not a supported " +
+ "process launch mechanism on this platform.");
+ }
+ }
+ });
+ }
+
+ /* this is for the reaping thread */
+ private native int waitForProcessExit(int pid);
+
+ /**
+ * Create a process. Depending on the mode flag, this is done by
+ * one of the following mechanisms.
+ * - fork(2) and exec(2)
+ * - clone(2) and exec(2)
+ * - vfork(2) and exec(2)
+ *
+ * @param fds an array of three file descriptors.
+ * Indexes 0, 1, and 2 correspond to standard input,
+ * standard output and standard error, respectively. On
+ * input, a value of -1 means to create a pipe to connect
+ * child and parent processes. On output, a value which
+ * is not -1 is the parent pipe fd corresponding to the
+ * pipe which has been created. An element of this array
+ * is -1 on input if and only if it is <em>not</em> -1 on
+ * output.
+ * @return the pid of the subprocess
+ */
+ private native int forkAndExec(int mode, byte[] helperpath,
+ byte[] prog,
+ byte[] argBlock, int argc,
+ byte[] envBlock, int envc,
+ byte[] dir,
+ int[] fds,
+ boolean redirectErrorStream)
+ throws IOException;
+
+ /**
+ * The thread factory used to create "process reaper" daemon threads.
+ */
+ private static class ProcessReaperThreadFactory implements ThreadFactory {
+ private final static ThreadGroup group = getRootThreadGroup();
+
+ private static ThreadGroup getRootThreadGroup() {
+ return doPrivileged(new PrivilegedAction<ThreadGroup> () {
+ public ThreadGroup run() {
+ ThreadGroup root = Thread.currentThread().getThreadGroup();
+ while (root.getParent() != null)
+ root = root.getParent();
+ return root;
+ }});
+ }
+
+ public Thread newThread(Runnable grimReaper) {
+ // Our thread stack requirement is quite modest.
+ Thread t = new Thread(group, grimReaper, "process reaper", 32768);
+ t.setDaemon(true);
+ // A small attempt (probably futile) to avoid priority inversion
+ t.setPriority(Thread.MAX_PRIORITY);
+ return t;
+ }
+ }
+
+ /**
+ * The thread pool of "process reaper" daemon threads.
+ */
+ private static final Executor processReaperExecutor =
+ doPrivileged(new PrivilegedAction<Executor>() {
+ public Executor run() {
+ return Executors.newCachedThreadPool
+ (new ProcessReaperThreadFactory());
+ }});
+
+ UNIXProcess(final byte[] prog,
+ final byte[] argBlock, final int argc,
+ final byte[] envBlock, final int envc,
+ final byte[] dir,
+ final int[] fds,
+ final boolean redirectErrorStream)
+ throws IOException {
+
+ pid = forkAndExec(launchMechanism.value,
+ helperpath,
+ prog,
+ argBlock, argc,
+ envBlock, envc,
+ dir,
+ fds,
+ redirectErrorStream);
+
+ try {
+ doPrivileged(new PrivilegedExceptionAction<Void>() {
+ public Void run() throws IOException {
+ initStreams(fds);
+ return null;
+ }});
+ } catch (PrivilegedActionException ex) {
+ throw (IOException) ex.getException();
+ }
+ }
+
+ static FileDescriptor newFileDescriptor(int fd) {
+ FileDescriptor fileDescriptor = new FileDescriptor();
+ fdAccess.set(fileDescriptor, fd);
+ return fileDescriptor;
+ }
+
+ void initStreams(int[] fds) throws IOException {
+ stdin = (fds[0] == -1) ?
+ ProcessBuilder.NullOutputStream.INSTANCE :
+ new ProcessPipeOutputStream(fds[0]);
+
+ stdout = (fds[1] == -1) ?
+ ProcessBuilder.NullInputStream.INSTANCE :
+ new ProcessPipeInputStream(fds[1]);
+
+ stderr = (fds[2] == -1) ?
+ ProcessBuilder.NullInputStream.INSTANCE :
+ new ProcessPipeInputStream(fds[2]);
+
+ processReaperExecutor.execute(new Runnable() {
+ public void run() {
+ int exitcode = waitForProcessExit(pid);
+ UNIXProcess.this.processExited(exitcode);
+ }});
+ }
+
+ void processExited(int exitcode) {
+ synchronized (this) {
+ this.exitcode = exitcode;
+ hasExited = true;
+ notifyAll();
+ }
+
+ if (stdout instanceof ProcessPipeInputStream)
+ ((ProcessPipeInputStream) stdout).processExited();
+
+ if (stderr instanceof ProcessPipeInputStream)
+ ((ProcessPipeInputStream) stderr).processExited();
+
+ if (stdin instanceof ProcessPipeOutputStream)
+ ((ProcessPipeOutputStream) stdin).processExited();
+ }
+
+ public OutputStream getOutputStream() {
+ return stdin;
+ }
+
+ public InputStream getInputStream() {
+ return stdout;
+ }
+
+ public InputStream getErrorStream() {
+ return stderr;
+ }
+
+ public synchronized int waitFor() throws InterruptedException {
+ while (!hasExited) {
+ wait();
+ }
+ return exitcode;
+ }
+
+ @Override
+ public synchronized boolean waitFor(long timeout, TimeUnit unit)
+ throws InterruptedException
+ {
+ if (hasExited) return true;
+ if (timeout <= 0) return false;
+
+ long timeoutAsNanos = unit.toNanos(timeout);
+ long startTime = System.nanoTime();
+ long rem = timeoutAsNanos;
+
+ while (!hasExited && (rem > 0)) {
+ wait(Math.max(TimeUnit.NANOSECONDS.toMillis(rem), 1));
+ rem = timeoutAsNanos - (System.nanoTime() - startTime);
+ }
+ return hasExited;
+ }
+
+ public synchronized int exitValue() {
+ if (!hasExited) {
+ throw new IllegalThreadStateException("process hasn't exited");
+ }
+ return exitcode;
+ }
+
+ private static native void destroyProcess(int pid, boolean force);
+ private void destroy(boolean force) {
+ // There is a risk that pid will be recycled, causing us to
+ // kill the wrong process! So we only terminate processes
+ // that appear to still be running. Even with this check,
+ // there is an unavoidable race condition here, but the window
+ // is very small, and OSes try hard to not recycle pids too
+ // soon, so this is quite safe.
+ synchronized (this) {
+ if (!hasExited)
+ destroyProcess(pid, force);
+ }
+ try { stdin.close(); } catch (IOException ignored) {}
+ try { stdout.close(); } catch (IOException ignored) {}
+ try { stderr.close(); } catch (IOException ignored) {}
+ }
+
+ public void destroy() {
+ destroy(false);
+ }
+
+ @Override
+ public Process destroyForcibly() {
+ destroy(true);
+ return this;
+ }
+
+ @Override
+ public synchronized boolean isAlive() {
+ return !hasExited;
+ }
+
+ private static native void init();
+
+ static {
+ init();
+ }
+
+ /**
+ * A buffered input stream for a subprocess pipe file descriptor
+ * that allows the underlying file descriptor to be reclaimed when
+ * the process exits, via the processExited hook.
+ *
+ * This is tricky because we do not want the user-level InputStream to be
+ * closed until the user invokes close(), and we need to continue to be
+ * able to read any buffered data lingering in the OS pipe buffer.
+ */
+ static class ProcessPipeInputStream extends BufferedInputStream {
+ private final Object closeLock = new Object();
+
+ ProcessPipeInputStream(int fd) {
+ super(new FileInputStream(newFileDescriptor(fd)));
+ }
+
+ private InputStream drainInputStream(InputStream in)
+ throws IOException {
+ int n = 0;
+ int j;
+ byte[] a = null;
+ synchronized (closeLock) {
+ if (buf == null) // asynchronous close()?
+ return null; // discard
+ j = in.available();
+ }
+ while (j > 0) {
+ a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j);
+ synchronized (closeLock) {
+ if (buf == null) // asynchronous close()?
+ return null; // discard
+ n += in.read(a, n, j);
+ j = in.available();
+ }
+ }
+ return (a == null) ?
+ ProcessBuilder.NullInputStream.INSTANCE :
+ new ByteArrayInputStream(n == a.length ? a : Arrays.copyOf(a, n));
+ }
+
+ /** Called by the process reaper thread when the process exits. */
+ synchronized void processExited() {
+ try {
+ InputStream in = this.in;
+ if (in != null) {
+ InputStream stragglers = drainInputStream(in);
+ in.close();
+ this.in = stragglers;
+ }
+ } catch (IOException ignored) { }
+ }
+
+ @Override
+ public void close() throws IOException {
+ // BufferedInputStream#close() is not synchronized unlike most other methods.
+ // Synchronizing helps avoid racing with drainInputStream().
+ synchronized (closeLock) {
+ super.close();
+ }
+ }
+ }
+
+ /**
+ * A buffered output stream for a subprocess pipe file descriptor
+ * that allows the underlying file descriptor to be reclaimed when
+ * the process exits, via the processExited hook.
+ */
+ static class ProcessPipeOutputStream extends BufferedOutputStream {
+ ProcessPipeOutputStream(int fd) {
+ super(new FileOutputStream(newFileDescriptor(fd)));
+ }
+
+ /** Called by the process reaper thread when the process exits. */
+ synchronized void processExited() {
+ OutputStream out = this.out;
+ if (out != null) {
+ try {
+ out.close();
+ } catch (IOException ignored) {
+ // We know of no reason to get an IOException, but if
+ // we do, there's nothing else to do but carry on.
+ }
+ this.out = ProcessBuilder.NullOutputStream.INSTANCE;
+ }
+ }
+ }
+}