1 // PosixProcess.java - Subclass of Process for POSIX systems.
2 /* Copyright (C) 1998, 1999, 2004, 2006, 2007 Free Software Foundation
4 This file is part of libgcj.
6 This software is copyrighted work licensed under the terms of the
7 Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
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
;
19 import gnu
.gcj
.RawDataManaged
;
22 * @author Tom Tromey <tromey@cygnus.com>
24 * @author David Daney <ddaney@avtrex.com> Rewrote using
27 final class PosixProcess
extends Process
29 static final class ProcessManager
extends Thread
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.
37 private LinkedList
<PosixProcess
> queue
= new LinkedList
<PosixProcess
>();
38 private LinkedList
<PosixProcess
> liveProcesses
=
39 new LinkedList
<PosixProcess
>();
40 private boolean ready
= false;
42 static RawDataManaged nativeData
;
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
51 super("ProcessManager", true);
52 // Don't keep the (main) process from exiting on our account.
57 * Add a process to the list of running processes. This must only
58 * be called with the queueLock held.
60 * @param p The PosixProcess.
62 void addToLiveProcesses(PosixProcess p
)
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.
72 * @param p The PosixProcess.
74 void startExecuting(PosixProcess p
)
76 synchronized (queueLock
)
79 signalReaper(); // If blocked in waitForSignal().
80 queueLock
.notifyAll(); // If blocked in wait();
85 * Block until the ProcessManager thread is ready to accept
97 catch (InterruptedException ie
)
105 * Main Process starting/reaping loop.
110 // Now ready to accept requests.
121 synchronized (queueLock
)
123 Iterator
<PosixProcess
> processIterator
=
124 liveProcesses
.iterator();
125 while (processIterator
.hasNext())
127 boolean reaped
= reap(processIterator
.next());
129 processIterator
.remove();
131 if (liveProcesses
.size() == 0 && queue
.size() == 0)
133 // This reaper thread could exit, but we keep it
134 // alive for a while in case someone wants to
135 // start more Processes.
138 queueLock
.wait(1000L);
139 if (queue
.size() == 0)
141 processManager
= null;
142 return; // Timed out.
145 catch (InterruptedException ie
)
147 // Ignore and exit the thread.
151 while (queue
.size() > 0)
153 PosixProcess p
= queue
.remove(0);
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.
166 ex
.printStackTrace(System
.err
);
172 * Setup native signal handlers and other housekeeping things.
174 private native void init();
177 * Block waiting for SIGCHLD.
180 private native void waitForSignal();
183 * Try to reap the specified child without blocking.
185 * @param p the process to try to reap.
187 * @return true if the process terminated.
190 private native boolean reap(PosixProcess p
);
193 * Send SIGCHLD to the reaper thread.
195 private native void signalReaper();
198 public void destroy()
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
)
210 // If there is no ProcessManager we cannot kill.
211 if (state
!= STATE_TERMINATED
)
213 if (processManager
== null)
214 throw new InternalError();
221 private native void nativeDestroy();
223 public int exitValue()
227 if (state
!= STATE_TERMINATED
)
228 throw new IllegalThreadStateException("Process has not exited");
234 * Called by native code when process exits.
236 * Already synchronized (this). Close any streams that we can to
237 * conserve file descriptors.
239 * The outputStream can be closed as any future writes will
240 * generate an IOException due to EPIPE.
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
248 void processTerminationCleanup()
252 outputStream
.close();
254 catch (IOException ioe
)
260 if (returnedErrorStream
== null && errorStream
.available() == 0)
266 catch (IOException ioe
)
272 if (returnedInputStream
== null && inputStream
.available() == 0)
278 catch (IOException ioe
)
284 public synchronized InputStream
getErrorStream()
286 if (returnedErrorStream
!= null)
287 return returnedErrorStream
;
289 if (errorStream
== null)
290 returnedErrorStream
= EOFInputStream
.instance
;
292 returnedErrorStream
= errorStream
;
294 return returnedErrorStream
;
297 public synchronized InputStream
getInputStream()
299 if (returnedInputStream
!= null)
300 return returnedInputStream
;
302 if (inputStream
== null)
303 returnedInputStream
= EOFInputStream
.instance
;
305 returnedInputStream
= inputStream
;
307 return returnedInputStream
;
310 public OutputStream
getOutputStream()
315 public int waitFor() throws InterruptedException
319 while (state
!= STATE_TERMINATED
)
326 * Start this process running. This should only be called by the
327 * ProcessManager with the queueLock held.
329 * @param pm The ProcessManager that made the call.
331 void spawn(ProcessManager pm
)
335 // Do the fork/exec magic.
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.
348 * Do the fork and exec.
350 private native void nativeSpawn();
352 PosixProcess(String
[] progarray
, String
[] envp
, File dir
, boolean redirect
)
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();
360 this.progarray
= progarray
;
363 this.redirect
= redirect
;
365 // Start a ProcessManager if there is not one already running.
366 synchronized (queueLock
)
368 if (processManager
== null)
370 processManager
= new ProcessManager();
371 processManager
.start();
372 processManager
.waitUntilReady();
375 // Queue this PosixProcess for starting by the ProcessManager.
376 processManager
.startExecuting(this);
379 // Wait until ProcessManager has started us.
382 while (state
== STATE_WAITING_TO_START
)
388 catch (InterruptedException ie
)
390 // FIXME: What to do when interrupted while blocking in a constructor?
396 // If there was a problem, re-throw it.
397 if (exception
!= null)
399 if (exception
instanceof IOException
)
401 IOException ioe
= new IOException(exception
.toString());
402 ioe
.initCause(exception
);
406 // Not an IOException. Something bad happened.
407 InternalError ie
= new InternalError(exception
.toString());
408 ie
.initCause(exception
);
412 // If we get here, all is well, the Process has started.
415 private String
[] progarray
;
416 private String
[] envp
;
418 private boolean redirect
;
420 /** Set by the ProcessManager on problems starting. */
421 private Throwable exception
;
423 /** The process id. This is cast to a pid_t on the native side. */
426 // FIXME: Why doesn't the friend declaration in PosixProcess.h
427 // allow PosixProcess$ProcessManager native code access these
428 // when they are private?
430 /** Before the process is forked. */
431 static final int STATE_WAITING_TO_START
= 0;
433 /** After the fork. */
434 static final int STATE_RUNNING
= 1;
436 /** After exit code has been collected. */
437 static final int STATE_TERMINATED
= 2;
439 /** One of STATE_WAITING_TO_START, STATE_RUNNING, STATE_TERMINATED. */
442 /** The exit status, if the child has exited. */
446 private InputStream errorStream
;
447 private InputStream inputStream
;
448 private OutputStream outputStream
;
450 /** InputStreams obtained by the user. Not null indicates that the
451 * user has obtained the stream.
453 private InputStream returnedErrorStream
;
454 private InputStream returnedInputStream
;
457 * Lock Object for all processManager related locking.
459 private static Object queueLock
= new Object();
460 private static ProcessManager processManager
;
462 static class EOFInputStream
extends InputStream
464 static EOFInputStream instance
= new EOFInputStream();