annotate src/solaris/classes/java/lang/UNIXProcess.java.linux @ 4338:9b8c96f96a0f

Added tag jdk7-b147 for changeset f097ca2434b1
author schien
date Mon, 27 Jun 2011 13:21:34 -0700
parents 48d7f8c4cd60
children
rev   line source
martin@25 1 /*
ohair@3261 2 * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
duke@0 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
duke@0 4 *
duke@0 5 * This code is free software; you can redistribute it and/or modify it
duke@0 6 * under the terms of the GNU General Public License version 2 only, as
ohair@2362 7 * published by the Free Software Foundation. Oracle designates this
duke@0 8 * particular file as subject to the "Classpath" exception as provided
ohair@2362 9 * by Oracle in the LICENSE file that accompanied this code.
duke@0 10 *
duke@0 11 * This code is distributed in the hope that it will be useful, but WITHOUT
duke@0 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
duke@0 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
duke@0 14 * version 2 for more details (a copy is included in the LICENSE file that
duke@0 15 * accompanied this code).
duke@0 16 *
duke@0 17 * You should have received a copy of the GNU General Public License version
duke@0 18 * 2 along with this work; if not, write to the Free Software Foundation,
duke@0 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
duke@0 20 *
ohair@2362 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
ohair@2362 22 * or visit www.oracle.com if you need additional information or have any
ohair@2362 23 * questions.
duke@0 24 */
duke@0 25
duke@0 26 package java.lang;
duke@0 27
martin@2473 28 import java.io.BufferedInputStream;
martin@2473 29 import java.io.BufferedOutputStream;
martin@2473 30 import java.io.ByteArrayInputStream;
martin@2473 31 import java.io.FileDescriptor;
martin@2473 32 import java.io.FileInputStream;
martin@2473 33 import java.io.FileOutputStream;
martin@2473 34 import java.io.IOException;
martin@2473 35 import java.io.InputStream;
martin@2473 36 import java.io.OutputStream;
martin@2473 37 import java.util.Arrays;
martin@2473 38 import java.util.concurrent.Executors;
martin@2473 39 import java.util.concurrent.Executor;
martin@2473 40 import java.util.concurrent.ThreadFactory;
martin@2473 41 import java.security.AccessController;
martin@2727 42 import static java.security.AccessController.doPrivileged;
martin@2473 43 import java.security.PrivilegedAction;
martin@2473 44 import java.security.PrivilegedActionException;
martin@2473 45 import java.security.PrivilegedExceptionAction;
duke@0 46
martin@2473 47 /**
martin@2473 48 * java.lang.Process subclass in the UNIX environment.
duke@0 49 *
duke@0 50 * @author Mario Wolczko and Ross Knippel.
duke@0 51 * @author Konstantin Kladko (ported to Linux)
martin@2473 52 * @author Martin Buchholz
duke@0 53 */
duke@0 54 final class UNIXProcess extends Process {
martin@25 55 private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
martin@25 56 = sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
martin@25 57
martin@2473 58 private final int pid;
duke@0 59 private int exitcode;
duke@0 60 private boolean hasExited;
duke@0 61
martin@2473 62 private /* final */ OutputStream stdin;
martin@2473 63 private /* final */ InputStream stdout;
martin@2473 64 private /* final */ InputStream stderr;
duke@0 65
duke@0 66 /* this is for the reaping thread */
duke@0 67 private native int waitForProcessExit(int pid);
duke@0 68
martin@25 69 /**
martin@25 70 * Create a process using fork(2) and exec(2).
martin@25 71 *
martin@2473 72 * @param fds an array of three file descriptors.
martin@2473 73 * Indexes 0, 1, and 2 correspond to standard input,
martin@2473 74 * standard output and standard error, respectively. On
martin@2473 75 * input, a value of -1 means to create a pipe to connect
martin@2473 76 * child and parent processes. On output, a value which
martin@2473 77 * is not -1 is the parent pipe fd corresponding to the
martin@2473 78 * pipe which has been created. An element of this array
martin@2473 79 * is -1 on input if and only if it is <em>not</em> -1 on
martin@2473 80 * output.
martin@25 81 * @return the pid of the subprocess
martin@25 82 */
duke@0 83 private native int forkAndExec(byte[] prog,
martin@25 84 byte[] argBlock, int argc,
martin@25 85 byte[] envBlock, int envc,
martin@25 86 byte[] dir,
martin@2473 87 int[] fds,
martin@25 88 boolean redirectErrorStream)
martin@25 89 throws IOException;
duke@0 90
martin@2473 91 /**
martin@2473 92 * The thread factory used to create "process reaper" daemon threads.
martin@2473 93 */
martin@2473 94 private static class ProcessReaperThreadFactory implements ThreadFactory {
martin@2473 95 private final static ThreadGroup group = getRootThreadGroup();
duke@0 96
martin@2473 97 private static ThreadGroup getRootThreadGroup() {
martin@2727 98 return doPrivileged(new PrivilegedAction<ThreadGroup> () {
martin@2727 99 public ThreadGroup run() {
martin@2727 100 ThreadGroup root = Thread.currentThread().getThreadGroup();
martin@2727 101 while (root.getParent() != null)
martin@2727 102 root = root.getParent();
martin@2727 103 return root;
martin@2727 104 }});
duke@0 105 }
duke@0 106
martin@2473 107 public Thread newThread(Runnable grimReaper) {
martin@2473 108 // Our thread stack requirement is quite modest.
martin@2473 109 Thread t = new Thread(group, grimReaper, "process reaper", 32768);
martin@2473 110 t.setDaemon(true);
martin@2473 111 // A small attempt (probably futile) to avoid priority inversion
martin@2473 112 t.setPriority(Thread.MAX_PRIORITY);
martin@2473 113 return t;
duke@0 114 }
duke@0 115 }
duke@0 116
martin@2473 117 /**
martin@2473 118 * The thread pool of "process reaper" daemon threads.
martin@2473 119 */
martin@2727 120 private static final Executor processReaperExecutor =
martin@2727 121 doPrivileged(new PrivilegedAction<Executor>() {
martin@2727 122 public Executor run() {
martin@2727 123 return Executors.newCachedThreadPool
martin@2727 124 (new ProcessReaperThreadFactory());
martin@2727 125 }});
martin@2473 126
duke@0 127 UNIXProcess(final byte[] prog,
martin@25 128 final byte[] argBlock, final int argc,
martin@25 129 final byte[] envBlock, final int envc,
martin@25 130 final byte[] dir,
martin@2473 131 final int[] fds,
martin@25 132 final boolean redirectErrorStream)
martin@2473 133 throws IOException {
duke@0 134
martin@2473 135 pid = forkAndExec(prog,
martin@2473 136 argBlock, argc,
martin@2473 137 envBlock, envc,
martin@2473 138 dir,
martin@2473 139 fds,
martin@2473 140 redirectErrorStream);
duke@0 141
martin@2473 142 try {
martin@2727 143 doPrivileged(new PrivilegedExceptionAction<Void>() {
martin@2473 144 public Void run() throws IOException {
martin@2473 145 initStreams(fds);
martin@2473 146 return null;
martin@2473 147 }});
martin@2473 148 } catch (PrivilegedActionException ex) {
martin@2473 149 throw (IOException) ex.getException();
martin@2473 150 }
martin@2473 151 }
martin@25 152
martin@2473 153 static FileDescriptor newFileDescriptor(int fd) {
martin@2473 154 FileDescriptor fileDescriptor = new FileDescriptor();
martin@2473 155 fdAccess.set(fileDescriptor, fd);
martin@2473 156 return fileDescriptor;
martin@2473 157 }
martin@25 158
martin@2473 159 void initStreams(int[] fds) throws IOException {
martin@2473 160 stdin = (fds[0] == -1) ?
martin@2473 161 ProcessBuilder.NullOutputStream.INSTANCE :
martin@2473 162 new ProcessPipeOutputStream(fds[0]);
martin@25 163
martin@2473 164 stdout = (fds[1] == -1) ?
martin@2473 165 ProcessBuilder.NullInputStream.INSTANCE :
martin@2473 166 new ProcessPipeInputStream(fds[1]);
martin@2473 167
martin@2473 168 stderr = (fds[2] == -1) ?
martin@2473 169 ProcessBuilder.NullInputStream.INSTANCE :
martin@2473 170 new ProcessPipeInputStream(fds[2]);
martin@2473 171
martin@2473 172 processReaperExecutor.execute(new Runnable() {
martin@2473 173 public void run() {
martin@2473 174 int exitcode = waitForProcessExit(pid);
martin@2473 175 UNIXProcess.this.processExited(exitcode);
martin@2473 176 }});
martin@2473 177 }
martin@2473 178
martin@2800 179 void processExited(int exitcode) {
martin@2800 180 synchronized (this) {
martin@2800 181 this.exitcode = exitcode;
martin@2800 182 hasExited = true;
martin@2800 183 notifyAll();
martin@2800 184 }
martin@2800 185
martin@2473 186 if (stdout instanceof ProcessPipeInputStream)
martin@2473 187 ((ProcessPipeInputStream) stdout).processExited();
martin@2473 188
martin@2473 189 if (stderr instanceof ProcessPipeInputStream)
martin@2473 190 ((ProcessPipeInputStream) stderr).processExited();
martin@2473 191
martin@2473 192 if (stdin instanceof ProcessPipeOutputStream)
martin@2473 193 ((ProcessPipeOutputStream) stdin).processExited();
duke@0 194 }
duke@0 195
duke@0 196 public OutputStream getOutputStream() {
martin@2473 197 return stdin;
duke@0 198 }
duke@0 199
duke@0 200 public InputStream getInputStream() {
martin@2473 201 return stdout;
duke@0 202 }
duke@0 203
duke@0 204 public InputStream getErrorStream() {
martin@2473 205 return stderr;
duke@0 206 }
duke@0 207
duke@0 208 public synchronized int waitFor() throws InterruptedException {
duke@0 209 while (!hasExited) {
martin@25 210 wait();
martin@25 211 }
martin@25 212 return exitcode;
duke@0 213 }
duke@0 214
duke@0 215 public synchronized int exitValue() {
martin@25 216 if (!hasExited) {
martin@25 217 throw new IllegalThreadStateException("process hasn't exited");
martin@25 218 }
martin@25 219 return exitcode;
duke@0 220 }
duke@0 221
duke@0 222 private static native void destroyProcess(int pid);
duke@0 223 public void destroy() {
martin@25 224 // There is a risk that pid will be recycled, causing us to
martin@25 225 // kill the wrong process! So we only terminate processes
martin@25 226 // that appear to still be running. Even with this check,
martin@25 227 // there is an unavoidable race condition here, but the window
martin@25 228 // is very small, and OSes try hard to not recycle pids too
martin@25 229 // soon, so this is quite safe.
martin@25 230 synchronized (this) {
martin@25 231 if (!hasExited)
martin@25 232 destroyProcess(pid);
martin@25 233 }
martin@2473 234 try { stdin.close(); } catch (IOException ignored) {}
martin@2473 235 try { stdout.close(); } catch (IOException ignored) {}
martin@2473 236 try { stderr.close(); } catch (IOException ignored) {}
duke@0 237 }
duke@0 238
duke@0 239 /* This routine initializes JNI field offsets for the class */
duke@0 240 private static native void initIDs();
duke@0 241
duke@0 242 static {
martin@25 243 initIDs();
duke@0 244 }
martin@2473 245
martin@2473 246 /**
martin@2473 247 * A buffered input stream for a subprocess pipe file descriptor
martin@2473 248 * that allows the underlying file descriptor to be reclaimed when
martin@2473 249 * the process exits, via the processExited hook.
martin@2473 250 *
martin@2473 251 * This is tricky because we do not want the user-level InputStream to be
martin@2473 252 * closed until the user invokes close(), and we need to continue to be
martin@2473 253 * able to read any buffered data lingering in the OS pipe buffer.
martin@2473 254 */
martin@2473 255 static class ProcessPipeInputStream extends BufferedInputStream {
martin@2473 256 ProcessPipeInputStream(int fd) {
martin@2473 257 super(new FileInputStream(newFileDescriptor(fd)));
martin@2473 258 }
martin@2473 259
martin@2473 260 private static byte[] drainInputStream(InputStream in)
martin@2473 261 throws IOException {
martin@2473 262 if (in == null) return null;
martin@2473 263 int n = 0;
martin@2473 264 int j;
martin@2473 265 byte[] a = null;
martin@2473 266 while ((j = in.available()) > 0) {
martin@2473 267 a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j);
martin@2473 268 n += in.read(a, n, j);
martin@2473 269 }
martin@2473 270 return (a == null || n == a.length) ? a : Arrays.copyOf(a, n);
martin@2473 271 }
martin@2473 272
martin@2473 273 /** Called by the process reaper thread when the process exits. */
martin@2473 274 synchronized void processExited() {
martin@2473 275 // Most BufferedInputStream methods are synchronized, but close()
martin@2473 276 // is not, and so we have to handle concurrent racing close().
martin@2473 277 try {
martin@2473 278 InputStream in = this.in;
martin@2473 279 if (in != null) {
martin@2473 280 byte[] stragglers = drainInputStream(in);
martin@2473 281 in.close();
martin@2473 282 this.in = (stragglers == null) ?
martin@2473 283 ProcessBuilder.NullInputStream.INSTANCE :
martin@2473 284 new ByteArrayInputStream(stragglers);
martin@2473 285 if (buf == null) // asynchronous close()?
martin@2473 286 this.in = null;
martin@2473 287 }
martin@2473 288 } catch (IOException ignored) {
martin@2473 289 // probably an asynchronous close().
martin@2473 290 }
martin@2473 291 }
martin@2473 292 }
martin@2473 293
martin@2473 294 /**
martin@2473 295 * A buffered output stream for a subprocess pipe file descriptor
martin@2473 296 * that allows the underlying file descriptor to be reclaimed when
martin@2473 297 * the process exits, via the processExited hook.
martin@2473 298 */
martin@2473 299 static class ProcessPipeOutputStream extends BufferedOutputStream {
martin@2473 300 ProcessPipeOutputStream(int fd) {
martin@2473 301 super(new FileOutputStream(newFileDescriptor(fd)));
martin@2473 302 }
martin@2473 303
martin@2473 304 /** Called by the process reaper thread when the process exits. */
martin@2473 305 synchronized void processExited() {
martin@2473 306 OutputStream out = this.out;
martin@2473 307 if (out != null) {
martin@2473 308 try {
martin@2473 309 out.close();
martin@2473 310 } catch (IOException ignored) {
martin@2473 311 // We know of no reason to get an IOException, but if
martin@2473 312 // we do, there's nothing else to do but carry on.
martin@2473 313 }
martin@2473 314 this.out = ProcessBuilder.NullOutputStream.INSTANCE;
martin@2473 315 }
martin@2473 316 }
martin@2473 317 }
duke@0 318 }