1 // PosixProcess.java - Subclass of Process for POSIX systems. 2 /* Copyright (C) 1998, 1999, 2004, 2006, 2007 Free Software Foundation 3 4 This file is part of libgcj. 5 6 This software is copyrighted work licensed under the terms of the 7 Libgcj License. Please consult the file "LIBGCJ_LICENSE" for 8 details. */ 9 10 package java.lang; 11 12 import java.io.File; 13 import java.io.IOException; 14 import java.io.InputStream; 15 import java.io.OutputStream; 16 import java.util.Iterator; 17 import java.util.LinkedList; 18 19 import gnu.gcj.RawDataManaged; 20 21 /** 22 * @author Tom Tromey <tromey@cygnus.com> 23 * @date May 3, 1999 24 * @author David Daney <ddaney@avtrex.com> Rewrote using 25 * ProcessManager 26 */ 27 final class PosixProcess extends Process 28 { 29 static final class ProcessManager extends Thread 30 { 31 /** 32 * A list of {@link PosixProcess PosixProcesses} to be 33 * started. The queueLock object is used as the lock Object 34 * for all process related operations. To avoid dead lock 35 * ensure queueLock is obtained before PosixProcess. 36 */ 37 private LinkedList<PosixProcess> queue = new LinkedList<PosixProcess>(); 38 private LinkedList<PosixProcess> liveProcesses = 39 new LinkedList<PosixProcess>(); 40 private boolean ready = false; 41 42 static RawDataManaged nativeData; 43 ProcessManager()44 ProcessManager() 45 { 46 // Use package private Thread constructor to place us in the 47 // root ThreadGroup with no InheritableThreadLocal. If the 48 // InheritableThreadLocals were allowed to initialize, they could 49 // cause a Runtime.exec() to be called causing infinite 50 // recursion. 51 super("ProcessManager", true); 52 // Don't keep the (main) process from exiting on our account. 53 this.setDaemon(true); 54 } 55 56 /** 57 * Add a process to the list of running processes. This must only 58 * be called with the queueLock held. 59 * 60 * @param p The PosixProcess. 61 */ addToLiveProcesses(PosixProcess p)62 void addToLiveProcesses(PosixProcess p) 63 { 64 liveProcesses.add(p); 65 } 66 67 /** 68 * Queue up the PosixProcess and awake the ProcessManager. 69 * The ProcessManager will start the PosixProcess from its 70 * thread so it can be reaped when it terminates. 71 * 72 * @param p The PosixProcess. 73 */ startExecuting(PosixProcess p)74 void startExecuting(PosixProcess p) 75 { 76 synchronized (queueLock) 77 { 78 queue.add(p); 79 signalReaper(); // If blocked in waitForSignal(). 80 queueLock.notifyAll(); // If blocked in wait(); 81 } 82 } 83 84 /** 85 * Block until the ProcessManager thread is ready to accept 86 * commands. 87 */ waitUntilReady()88 void waitUntilReady() 89 { 90 synchronized (this) 91 { 92 try 93 { 94 while (! ready) 95 wait(); 96 } 97 catch (InterruptedException ie) 98 { 99 // Ignore. 100 } 101 } 102 } 103 104 /** 105 * Main Process starting/reaping loop. 106 */ run()107 public void run() 108 { 109 init(); 110 // Now ready to accept requests. 111 synchronized (this) 112 { 113 ready = true; 114 this.notifyAll(); 115 } 116 117 for (;;) 118 { 119 try 120 { 121 synchronized (queueLock) 122 { 123 Iterator<PosixProcess> processIterator = 124 liveProcesses.iterator(); 125 while (processIterator.hasNext()) 126 { 127 boolean reaped = reap(processIterator.next()); 128 if (reaped) 129 processIterator.remove(); 130 } 131 if (liveProcesses.size() == 0 && queue.size() == 0) 132 { 133 // This reaper thread could exit, but we keep it 134 // alive for a while in case someone wants to 135 // start more Processes. 136 try 137 { 138 queueLock.wait(1000L); 139 if (queue.size() == 0) 140 { 141 processManager = null; 142 return; // Timed out. 143 } 144 } 145 catch (InterruptedException ie) 146 { 147 // Ignore and exit the thread. 148 return; 149 } 150 } 151 while (queue.size() > 0) 152 { 153 PosixProcess p = queue.remove(0); 154 p.spawn(this); 155 } 156 } 157 158 // Wait for a SIGCHLD from either an exiting process or 159 // the startExecuting() method. This is done outside of 160 // the synchronized block to allow other threads to 161 // enter and submit more jobs. 162 waitForSignal(); 163 } 164 catch (Exception ex) 165 { 166 ex.printStackTrace(System.err); 167 } 168 } 169 } 170 171 /** 172 * Setup native signal handlers and other housekeeping things. 173 */ init()174 private native void init(); 175 176 /** 177 * Block waiting for SIGCHLD. 178 * 179 */ waitForSignal()180 private native void waitForSignal(); 181 182 /** 183 * Try to reap the specified child without blocking. 184 * 185 * @param p the process to try to reap. 186 * 187 * @return true if the process terminated. 188 * 189 */ reap(PosixProcess p)190 private native boolean reap(PosixProcess p); 191 192 /** 193 * Send SIGCHLD to the reaper thread. 194 */ signalReaper()195 private native void signalReaper(); 196 } 197 destroy()198 public void destroy() 199 { 200 // Synchronized on the queueLock. This ensures that the reaper 201 // thread cannot be doing a wait() on the child. 202 // Otherwise there would be a race where the OS could 203 // create a process with the same pid between the wait() 204 // and the update of the state which would cause a kill to 205 // the wrong process. 206 synchronized (queueLock) 207 { 208 synchronized (this) 209 { 210 // If there is no ProcessManager we cannot kill. 211 if (state != STATE_TERMINATED) 212 { 213 if (processManager == null) 214 throw new InternalError(); 215 nativeDestroy(); 216 } 217 } 218 } 219 } 220 nativeDestroy()221 private native void nativeDestroy(); 222 exitValue()223 public int exitValue() 224 { 225 synchronized (this) 226 { 227 if (state != STATE_TERMINATED) 228 throw new IllegalThreadStateException("Process has not exited"); 229 } 230 return status; 231 } 232 233 /** 234 * Called by native code when process exits. 235 * 236 * Already synchronized (this). Close any streams that we can to 237 * conserve file descriptors. 238 * 239 * The outputStream can be closed as any future writes will 240 * generate an IOException due to EPIPE. 241 * 242 * The inputStream and errorStream can only be closed if the user 243 * has not obtained a reference to them AND they have no bytes 244 * available. Since the process has terminated they will never have 245 * any more data available and can safely be replaced by 246 * EOFInputStreams. 247 */ processTerminationCleanup()248 void processTerminationCleanup() 249 { 250 try 251 { 252 outputStream.close(); 253 } 254 catch (IOException ioe) 255 { 256 // Ignore. 257 } 258 try 259 { 260 if (returnedErrorStream == null && errorStream.available() == 0) 261 { 262 errorStream.close(); 263 errorStream = null; 264 } 265 } 266 catch (IOException ioe) 267 { 268 // Ignore. 269 } 270 try 271 { 272 if (returnedInputStream == null && inputStream.available() == 0) 273 { 274 inputStream.close(); 275 inputStream = null; 276 } 277 } 278 catch (IOException ioe) 279 { 280 // Ignore. 281 } 282 } 283 getErrorStream()284 public synchronized InputStream getErrorStream() 285 { 286 if (returnedErrorStream != null) 287 return returnedErrorStream; 288 289 if (errorStream == null) 290 returnedErrorStream = EOFInputStream.instance; 291 else 292 returnedErrorStream = errorStream; 293 294 return returnedErrorStream; 295 } 296 getInputStream()297 public synchronized InputStream getInputStream() 298 { 299 if (returnedInputStream != null) 300 return returnedInputStream; 301 302 if (inputStream == null) 303 returnedInputStream = EOFInputStream.instance; 304 else 305 returnedInputStream = inputStream; 306 307 return returnedInputStream; 308 } 309 getOutputStream()310 public OutputStream getOutputStream() 311 { 312 return outputStream; 313 } 314 waitFor()315 public int waitFor() throws InterruptedException 316 { 317 synchronized (this) 318 { 319 while (state != STATE_TERMINATED) 320 wait(); 321 } 322 return status; 323 } 324 325 /** 326 * Start this process running. This should only be called by the 327 * ProcessManager with the queueLock held. 328 * 329 * @param pm The ProcessManager that made the call. 330 */ spawn(ProcessManager pm)331 void spawn(ProcessManager pm) 332 { 333 synchronized (this) 334 { 335 // Do the fork/exec magic. 336 nativeSpawn(); 337 // There is no race with reap() in the pidToProcess map 338 // because this is always called from the same thread 339 // doing the reaping. 340 pm.addToLiveProcesses(this); 341 state = STATE_RUNNING; 342 // Notify anybody waiting on state change. 343 this.notifyAll(); 344 } 345 } 346 347 /** 348 * Do the fork and exec. 349 */ nativeSpawn()350 private native void nativeSpawn(); 351 PosixProcess(String[] progarray, String[] envp, File dir, boolean redirect)352 PosixProcess(String[] progarray, String[] envp, File dir, boolean redirect) 353 throws IOException 354 { 355 // Check to ensure there is something to run, and avoid 356 // dereferencing null pointers in native code. 357 if (progarray[0] == null) 358 throw new NullPointerException(); 359 360 this.progarray = progarray; 361 this.envp = envp; 362 this.dir = dir; 363 this.redirect = redirect; 364 365 // Start a ProcessManager if there is not one already running. 366 synchronized (queueLock) 367 { 368 if (processManager == null) 369 { 370 processManager = new ProcessManager(); 371 processManager.start(); 372 processManager.waitUntilReady(); 373 } 374 375 // Queue this PosixProcess for starting by the ProcessManager. 376 processManager.startExecuting(this); 377 } 378 379 // Wait until ProcessManager has started us. 380 synchronized (this) 381 { 382 while (state == STATE_WAITING_TO_START) 383 { 384 try 385 { 386 wait(); 387 } 388 catch (InterruptedException ie) 389 { 390 // FIXME: What to do when interrupted while blocking in a constructor? 391 // Ignore. 392 } 393 } 394 } 395 396 // If there was a problem, re-throw it. 397 if (exception != null) 398 { 399 if (exception instanceof IOException) 400 { 401 IOException ioe = new IOException(exception.toString()); 402 ioe.initCause(exception); 403 throw ioe; 404 } 405 406 // Not an IOException. Something bad happened. 407 InternalError ie = new InternalError(exception.toString()); 408 ie.initCause(exception); 409 throw ie; 410 } 411 412 // If we get here, all is well, the Process has started. 413 } 414 415 private String[] progarray; 416 private String[] envp; 417 private File dir; 418 private boolean redirect; 419 420 /** Set by the ProcessManager on problems starting. */ 421 private Throwable exception; 422 423 /** The process id. This is cast to a pid_t on the native side. */ 424 long pid; 425 426 // FIXME: Why doesn't the friend declaration in PosixProcess.h 427 // allow PosixProcess$ProcessManager native code access these 428 // when they are private? 429 430 /** Before the process is forked. */ 431 static final int STATE_WAITING_TO_START = 0; 432 433 /** After the fork. */ 434 static final int STATE_RUNNING = 1; 435 436 /** After exit code has been collected. */ 437 static final int STATE_TERMINATED = 2; 438 439 /** One of STATE_WAITING_TO_START, STATE_RUNNING, STATE_TERMINATED. */ 440 int state; 441 442 /** The exit status, if the child has exited. */ 443 int status; 444 445 /** The streams. */ 446 private InputStream errorStream; 447 private InputStream inputStream; 448 private OutputStream outputStream; 449 450 /** InputStreams obtained by the user. Not null indicates that the 451 * user has obtained the stream. 452 */ 453 private InputStream returnedErrorStream; 454 private InputStream returnedInputStream; 455 456 /** 457 * Lock Object for all processManager related locking. 458 */ 459 private static Object queueLock = new Object(); 460 private static ProcessManager processManager; 461 462 static class EOFInputStream extends InputStream 463 { 464 static EOFInputStream instance = new EOFInputStream(); read()465 public int read() 466 { 467 return -1; 468 } 469 } 470 } 471