jdk/src/solaris/classes/java/lang/UNIXProcess.java.solaris
changeset 24407 86ebcdad9698
parent 24406 076ae2d12410
parent 24390 cfe99e4d318b
child 24408 97932f6ad950
equal deleted inserted replaced
24406:076ae2d12410 24407:86ebcdad9698
     1 /*
       
     2  * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package java.lang;
       
    27 
       
    28 import java.io.*;
       
    29 import java.util.concurrent.TimeUnit;
       
    30 import java.security.AccessController;
       
    31 import java.security.PrivilegedAction;
       
    32 
       
    33 /* java.lang.Process subclass in the UNIX environment.
       
    34  *
       
    35  * @author Mario Wolczko and Ross Knippel.
       
    36  */
       
    37 
       
    38 final class UNIXProcess extends Process {
       
    39     private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
       
    40         = sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
       
    41 
       
    42     private final int pid;
       
    43     private int exitcode;
       
    44     private boolean hasExited;
       
    45 
       
    46     private OutputStream stdin_stream;
       
    47     private InputStream stdout_stream;
       
    48     private DeferredCloseInputStream stdout_inner_stream;
       
    49     private InputStream stderr_stream;
       
    50 
       
    51     private static enum LaunchMechanism {
       
    52         FORK(1),
       
    53         POSIX_SPAWN(2);
       
    54 
       
    55         private int value;
       
    56         LaunchMechanism(int x) {value = x;}
       
    57     };
       
    58 
       
    59     /* On Solaris, the default is to spawn */
       
    60     private static final LaunchMechanism launchMechanism;
       
    61     private static byte[] helperpath;
       
    62 
       
    63     private static byte[] toCString(String s) {
       
    64         if (s == null)
       
    65             return null;
       
    66         byte[] bytes = s.getBytes();
       
    67         byte[] result = new byte[bytes.length + 1];
       
    68         System.arraycopy(bytes, 0,
       
    69                          result, 0,
       
    70                          bytes.length);
       
    71         result[result.length-1] = (byte)0;
       
    72         return result;
       
    73     }
       
    74 
       
    75     static {
       
    76         launchMechanism = AccessController.doPrivileged(
       
    77                 new PrivilegedAction<LaunchMechanism>()
       
    78         {
       
    79             public LaunchMechanism run() {
       
    80                 String javahome = System.getProperty("java.home");
       
    81                 String osArch = System.getProperty("os.arch");
       
    82                 if (osArch.equals("x86")) {
       
    83                     osArch = "i386";
       
    84                 } else if (osArch.equals("x86_64")) {
       
    85                     osArch = "amd64";
       
    86                 }
       
    87 
       
    88                 helperpath = toCString(javahome + "/lib/" + osArch + "/jspawnhelper");
       
    89                 String s = System.getProperty(
       
    90                     "jdk.lang.Process.launchMechanism", "posix_spawn");
       
    91 
       
    92                 try {
       
    93                     return LaunchMechanism.valueOf(s.toUpperCase());
       
    94                 } catch (IllegalArgumentException e) {
       
    95                     throw new Error(s + " is not a supported " +
       
    96                         "process launch mechanism on this platform.");
       
    97                 }
       
    98             }
       
    99         });
       
   100     }
       
   101 
       
   102     /* this is for the reaping thread */
       
   103     private native int waitForProcessExit(int pid);
       
   104 
       
   105     /**
       
   106      * Create a process. Depending on the mode flag, this is done by
       
   107      * one of the following mechanisms.
       
   108      * - fork(2) and exec(2)
       
   109      * - posix_spawn(2)
       
   110      *
       
   111      * @param std_fds array of file descriptors.  Indexes 0, 1, and
       
   112      *        2 correspond to standard input, standard output and
       
   113      *        standard error, respectively.  On input, a value of -1
       
   114      *        means to create a pipe to connect child and parent
       
   115      *        processes.  On output, a value which is not -1 is the
       
   116      *        parent pipe fd corresponding to the pipe which has
       
   117      *        been created.  An element of this array is -1 on input
       
   118      *        if and only if it is <em>not</em> -1 on output.
       
   119      * @return the pid of the subprocess
       
   120      */
       
   121     private native int forkAndExec(int mode, byte[] helperpath,
       
   122                                    byte[] prog,
       
   123                                    byte[] argBlock, int argc,
       
   124                                    byte[] envBlock, int envc,
       
   125                                    byte[] dir,
       
   126                                    int[] std_fds,
       
   127                                    boolean redirectErrorStream)
       
   128         throws IOException;
       
   129 
       
   130     UNIXProcess(final byte[] prog,
       
   131                 final byte[] argBlock, int argc,
       
   132                 final byte[] envBlock, int envc,
       
   133                 final byte[] dir,
       
   134                 final int[] std_fds,
       
   135                 final boolean redirectErrorStream)
       
   136     throws IOException {
       
   137         pid = forkAndExec(launchMechanism.value,
       
   138                           helperpath,
       
   139                           prog,
       
   140                           argBlock, argc,
       
   141                           envBlock, envc,
       
   142                           dir,
       
   143                           std_fds,
       
   144                           redirectErrorStream);
       
   145 
       
   146         java.security.AccessController.doPrivileged(
       
   147         new java.security.PrivilegedAction<Void>() { public Void run() {
       
   148             if (std_fds[0] == -1)
       
   149                 stdin_stream = ProcessBuilder.NullOutputStream.INSTANCE;
       
   150             else {
       
   151                 FileDescriptor stdin_fd = new FileDescriptor();
       
   152                 fdAccess.set(stdin_fd, std_fds[0]);
       
   153                 stdin_stream = new BufferedOutputStream(
       
   154                     new FileOutputStream(stdin_fd));
       
   155             }
       
   156 
       
   157             if (std_fds[1] == -1)
       
   158                 stdout_stream = ProcessBuilder.NullInputStream.INSTANCE;
       
   159             else {
       
   160                 FileDescriptor stdout_fd = new FileDescriptor();
       
   161                 fdAccess.set(stdout_fd, std_fds[1]);
       
   162                 stdout_inner_stream = new DeferredCloseInputStream(stdout_fd);
       
   163                 stdout_stream = new BufferedInputStream(stdout_inner_stream);
       
   164             }
       
   165 
       
   166             if (std_fds[2] == -1)
       
   167                 stderr_stream = ProcessBuilder.NullInputStream.INSTANCE;
       
   168             else {
       
   169                 FileDescriptor stderr_fd = new FileDescriptor();
       
   170                 fdAccess.set(stderr_fd, std_fds[2]);
       
   171                 stderr_stream = new DeferredCloseInputStream(stderr_fd);
       
   172             }
       
   173 
       
   174             return null; }});
       
   175 
       
   176         /*
       
   177          * For each subprocess forked a corresponding reaper thread
       
   178          * is started.  That thread is the only thread which waits
       
   179          * for the subprocess to terminate and it doesn't hold any
       
   180          * locks while doing so.  This design allows waitFor() and
       
   181          * exitStatus() to be safely executed in parallel (and they
       
   182          * need no native code).
       
   183          */
       
   184 
       
   185         java.security.AccessController.doPrivileged(
       
   186             new java.security.PrivilegedAction<Void>() { public Void run() {
       
   187                 Thread t = new Thread("process reaper") {
       
   188                     public void run() {
       
   189                         int res = waitForProcessExit(pid);
       
   190                         synchronized (UNIXProcess.this) {
       
   191                             hasExited = true;
       
   192                             exitcode = res;
       
   193                             UNIXProcess.this.notifyAll();
       
   194                         }
       
   195                     }
       
   196                 };
       
   197                 t.setDaemon(true);
       
   198                 t.start();
       
   199                 return null; }});
       
   200     }
       
   201 
       
   202     public OutputStream getOutputStream() {
       
   203         return stdin_stream;
       
   204     }
       
   205 
       
   206     public InputStream getInputStream() {
       
   207         return stdout_stream;
       
   208     }
       
   209 
       
   210     public InputStream getErrorStream() {
       
   211         return stderr_stream;
       
   212     }
       
   213 
       
   214     public synchronized int waitFor() throws InterruptedException {
       
   215         while (!hasExited) {
       
   216             wait();
       
   217         }
       
   218         return exitcode;
       
   219     }
       
   220 
       
   221     @Override
       
   222     public synchronized boolean waitFor(long timeout, TimeUnit unit)
       
   223         throws InterruptedException
       
   224     {
       
   225         if (hasExited) return true;
       
   226         if (timeout <= 0) return false;
       
   227 
       
   228         long timeoutAsNanos = unit.toNanos(timeout);
       
   229         long startTime = System.nanoTime();
       
   230         long rem = timeoutAsNanos;
       
   231 
       
   232         while (!hasExited && (rem > 0)) {
       
   233             wait(Math.max(TimeUnit.NANOSECONDS.toMillis(rem), 1));
       
   234             rem = timeoutAsNanos - (System.nanoTime() - startTime);
       
   235         }
       
   236         return hasExited;
       
   237     }
       
   238 
       
   239     public synchronized int exitValue() {
       
   240         if (!hasExited) {
       
   241             throw new IllegalThreadStateException("process hasn't exited");
       
   242         }
       
   243         return exitcode;
       
   244     }
       
   245 
       
   246     private static native void destroyProcess(int pid, boolean force);
       
   247     private synchronized void destroy(boolean force) {
       
   248         // There is a risk that pid will be recycled, causing us to
       
   249         // kill the wrong process!  So we only terminate processes
       
   250         // that appear to still be running.  Even with this check,
       
   251         // there is an unavoidable race condition here, but the window
       
   252         // is very small, and OSes try hard to not recycle pids too
       
   253         // soon, so this is quite safe.
       
   254         if (!hasExited)
       
   255             destroyProcess(pid, force);
       
   256         try {
       
   257             stdin_stream.close();
       
   258             if (stdout_inner_stream != null)
       
   259                 stdout_inner_stream.closeDeferred(stdout_stream);
       
   260             if (stderr_stream instanceof DeferredCloseInputStream)
       
   261                 ((DeferredCloseInputStream) stderr_stream)
       
   262                     .closeDeferred(stderr_stream);
       
   263         } catch (IOException e) {
       
   264             // ignore
       
   265         }
       
   266     }
       
   267 
       
   268     public void destroy() {
       
   269         destroy(false);
       
   270     }
       
   271 
       
   272     @Override
       
   273     public Process destroyForcibly() {
       
   274         destroy(true);
       
   275         return this;
       
   276     }
       
   277 
       
   278     @Override
       
   279     public synchronized boolean isAlive() {
       
   280         return !hasExited;
       
   281     }
       
   282 
       
   283     // A FileInputStream that supports the deferment of the actual close
       
   284     // operation until the last pending I/O operation on the stream has
       
   285     // finished.  This is required on Solaris because we must close the stdin
       
   286     // and stdout streams in the destroy method in order to reclaim the
       
   287     // underlying file descriptors.  Doing so, however, causes any thread
       
   288     // currently blocked in a read on one of those streams to receive an
       
   289     // IOException("Bad file number"), which is incompatible with historical
       
   290     // behavior.  By deferring the close we allow any pending reads to see -1
       
   291     // (EOF) as they did before.
       
   292     //
       
   293     private static class DeferredCloseInputStream
       
   294         extends FileInputStream
       
   295     {
       
   296 
       
   297         private DeferredCloseInputStream(FileDescriptor fd) {
       
   298             super(fd);
       
   299         }
       
   300 
       
   301         private Object lock = new Object();     // For the following fields
       
   302         private boolean closePending = false;
       
   303         private int useCount = 0;
       
   304         private InputStream streamToClose;
       
   305 
       
   306         private void raise() {
       
   307             synchronized (lock) {
       
   308                 useCount++;
       
   309             }
       
   310         }
       
   311 
       
   312         private void lower() throws IOException {
       
   313             synchronized (lock) {
       
   314                 useCount--;
       
   315                 if (useCount == 0 && closePending) {
       
   316                     streamToClose.close();
       
   317                 }
       
   318             }
       
   319         }
       
   320 
       
   321         // stc is the actual stream to be closed; it might be this object, or
       
   322         // it might be an upstream object for which this object is downstream.
       
   323         //
       
   324         private void closeDeferred(InputStream stc) throws IOException {
       
   325             synchronized (lock) {
       
   326                 if (useCount == 0) {
       
   327                     stc.close();
       
   328                 } else {
       
   329                     closePending = true;
       
   330                     streamToClose = stc;
       
   331                 }
       
   332             }
       
   333         }
       
   334 
       
   335         public void close() throws IOException {
       
   336             synchronized (lock) {
       
   337                 useCount = 0;
       
   338                 closePending = false;
       
   339             }
       
   340             super.close();
       
   341         }
       
   342 
       
   343         public int read() throws IOException {
       
   344             raise();
       
   345             try {
       
   346                 return super.read();
       
   347             } finally {
       
   348                 lower();
       
   349             }
       
   350         }
       
   351 
       
   352         public int read(byte[] b) throws IOException {
       
   353             raise();
       
   354             try {
       
   355                 return super.read(b);
       
   356             } finally {
       
   357                 lower();
       
   358             }
       
   359         }
       
   360 
       
   361         public int read(byte[] b, int off, int len) throws IOException {
       
   362             raise();
       
   363             try {
       
   364                 return super.read(b, off, len);
       
   365             } finally {
       
   366                 lower();
       
   367             }
       
   368         }
       
   369 
       
   370         public long skip(long n) throws IOException {
       
   371             raise();
       
   372             try {
       
   373                 return super.skip(n);
       
   374             } finally {
       
   375                 lower();
       
   376             }
       
   377         }
       
   378 
       
   379         public int available() throws IOException {
       
   380             raise();
       
   381             try {
       
   382                 return super.available();
       
   383             } finally {
       
   384                 lower();
       
   385             }
       
   386         }
       
   387 
       
   388     }
       
   389 
       
   390     private static native void init();
       
   391 
       
   392     static {
       
   393         init();
       
   394     }
       
   395 }