1 // PosixProcess.java - Subclass of Process for POSIX systems.
2 /* Copyright (C) 1998, 1999, 2004 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
.HashMap
;
17 import java
.util
.LinkedList
;
18 import java
.util
.List
;
23 * @author Tom Tromey <tromey@cygnus.com>
25 * @author David Daney <ddaney@avtrex.com> Rewrote using
29 // This is entirely internal to our implementation.
30 // This file is copied to `ConcreteProcess.java' before compilation.
31 // Hence the class name apparently does not match the file name.
32 final class ConcreteProcess
extends Process
34 static class ProcessManager
extends Thread
37 * A list of {@link ConcreteProcess ConcreteProcesses} to be
38 * started. The queueLock object is used as the lock Object
39 * for all process related operations. To avoid dead lock
40 * ensure queueLock is obtained before ConcreteProcess.
42 List queue
= new LinkedList();
43 private Map pidToProcess
= new HashMap();
44 private boolean ready
= false;
45 private long reaperPID
;
49 super("ProcessManager");
50 // Don't keep the (main) process from exiting on our account.
55 * Get the ConcreteProcess object with the given pid and
56 * remove it from the map. This method is called from the
57 * native code for {@link #reap()). The mapping is removed so
58 * the ConcreteProcesses can be GCed after they terminate.
60 * @param p The pid of the process.
62 private ConcreteProcess
removeProcessFromMap(long p
)
64 return (ConcreteProcess
) pidToProcess
.remove(new Long(p
));
68 * Put the given ConcreteProcess in the map using the Long
69 * value of its pid as the key.
71 * @param p The ConcreteProcess.
73 void addProcessToMap(ConcreteProcess p
)
75 pidToProcess
.put(new Long(p
.pid
), p
);
79 * Queue up the ConcreteProcess and awake the ProcessManager.
80 * The ProcessManager will start the ConcreteProcess from its
81 * thread so it can be reaped when it terminates.
83 * @param p The ConcreteProcess.
85 void startExecuting(ConcreteProcess p
)
87 synchronized (queueLock
)
90 signalReaper(); // If blocked in waitForSignal().
91 queueLock
.notifyAll(); // If blocked in wait();
96 * Block until the ProcessManager thread is ready to accept
108 catch (InterruptedException ie
)
116 * Main Process starting/reaping loop.
121 // Now ready to accept requests.
132 synchronized (queueLock
)
134 boolean haveMoreChildren
= reap();
135 if (! haveMoreChildren
&& queue
.size() == 0)
137 // This reaper thread could exit, but we
138 // keep it alive for a while in case
139 // someone wants to start more Processes.
142 queueLock
.wait(1000L);
143 if (queue
.size() == 0)
145 processManager
= null;
146 return; // Timed out.
149 catch (InterruptedException ie
)
151 // Ignore and exit the thread.
155 while (queue
.size() > 0)
157 ConcreteProcess p
= (ConcreteProcess
) queue
.remove(0);
162 // Wait for a SIGCHLD from either an exiting
163 // process or the startExecuting() method. This
164 // is done outside of the synchronized block to
165 // allow other threads to enter and submit more
171 ex
.printStackTrace(System
.err
);
177 * Setup native signal handlers and other housekeeping things.
180 private native void init();
183 * Block waiting for SIGCHLD.
186 private native void waitForSignal();
189 * Try to reap as many children as possible without blocking.
191 * @return true if more live children exist.
194 private native boolean reap();
197 * Send SIGCHLD to the reaper thread.
199 private native void signalReaper();
202 public void destroy()
204 // Synchronized on the queueLock. This ensures that the reaper
205 // thread cannot be doing a wait() on the child.
206 // Otherwise there would be a race where the OS could
207 // create a process with the same pid between the wait()
208 // and the update of the state which would cause a kill to
209 // the wrong process.
210 synchronized (queueLock
)
214 // If there is no ProcessManager we cannot kill.
215 if (state
!= STATE_TERMINATED
)
217 if (processManager
== null)
218 throw new InternalError();
225 private native void nativeDestroy();
227 public int exitValue()
231 if (state
!= STATE_TERMINATED
)
232 throw new IllegalThreadStateException("Process has not exited");
238 * Called by native code when process exits.
240 * Already synchronized (this). Close any streams that we can to
241 * conserve file descriptors.
243 * The outputStream can be closed as any future writes will
244 * generate an IOException due to EPIPE.
246 * The inputStream and errorStream can only be closed if the user
247 * has not obtained a reference to them AND they have no bytes
248 * available. Since the process has terminated they will never have
249 * any more data available and can safely be replaced by
252 void processTerminationCleanup()
256 outputStream
.close();
258 catch (IOException ioe
)
264 if (returnedErrorStream
== null && errorStream
.available() == 0)
270 catch (IOException ioe
)
276 if (returnedInputStream
== null && inputStream
.available() == 0)
282 catch (IOException ioe
)
288 public synchronized InputStream
getErrorStream()
290 if (returnedErrorStream
!= null)
291 return returnedErrorStream
;
293 if (errorStream
== null)
294 returnedErrorStream
= EOFInputStream
.instance
;
296 returnedErrorStream
= errorStream
;
298 return returnedErrorStream
;
301 public synchronized InputStream
getInputStream()
303 if (returnedInputStream
!= null)
304 return returnedInputStream
;
306 if (inputStream
== null)
307 returnedInputStream
= EOFInputStream
.instance
;
309 returnedInputStream
= inputStream
;
311 return returnedInputStream
;
314 public OutputStream
getOutputStream()
319 public int waitFor() throws InterruptedException
323 while (state
!= STATE_TERMINATED
)
330 * Start this process running. This should only be called by the
333 * @param pm The ProcessManager that made the call.
335 void spawn(ProcessManager pm
)
339 // Do the fork/exec magic.
341 // There is no race with reap() in the pidToProcess map
342 // because this is always called from the same thread
343 // doing the reaping.
344 pm
.addProcessToMap(this);
345 state
= STATE_RUNNING
;
346 // Notify anybody waiting on state change.
352 * Do the fork and exec.
354 private native void nativeSpawn();
356 // This file is copied to `ConcreteProcess.java' before
357 // compilation. Hence the constructor name apparently does not
358 // match the file name.
359 ConcreteProcess(String
[] progarray
, String
[] envp
, File dir
)
362 // Check to ensure there is something to run, and avoid
363 // dereferencing null pointers in native code.
364 if (progarray
[0] == null)
365 throw new NullPointerException();
367 this.progarray
= progarray
;
371 // Start a ProcessManager if there is not one already running.
372 synchronized (queueLock
)
374 if (processManager
== null)
376 processManager
= new ProcessManager();
377 processManager
.start();
378 processManager
.waitUntilReady();
381 // Queue this ConcreteProcess for starting by the ProcessManager.
382 processManager
.startExecuting(this);
385 // Wait until ProcessManager has started us.
388 while (state
== STATE_WAITING_TO_START
)
394 catch (InterruptedException ie
)
396 // FIXME: What to do when interrupted while blocking in a constructor?
402 // If there was a problem, re-throw it.
403 if (exception
!= null)
405 if (exception
instanceof IOException
)
407 IOException ioe
= new IOException(exception
.toString());
408 ioe
.initCause(exception
);
412 // Not an IOException. Something bad happened.
413 InternalError ie
= new InternalError(exception
.toString());
414 ie
.initCause(exception
);
418 // If we get here, all is well, the Process has started.
421 private String
[] progarray
;
422 private String
[] envp
;
425 /** Set by the ProcessManager on problems starting. */
426 private Throwable exception
;
428 /** The process id. This is cast to a pid_t on the native side. */
431 // FIXME: Why doesn't the friend declaration in ConcreteProcess.h
432 // allow ConcreteProcess$ProcessManager native code access these
433 // when they are private?
435 /** Before the process is forked. */
436 static final int STATE_WAITING_TO_START
= 0;
438 /** After the fork. */
439 static final int STATE_RUNNING
= 1;
441 /** After exit code has been collected. */
442 static final int STATE_TERMINATED
= 2;
444 /** One of STATE_WAITING_TO_START, STATE_RUNNING, STATE_TERMINATED. */
447 /** The exit status, if the child has exited. */
451 private InputStream errorStream
;
452 private InputStream inputStream
;
453 private OutputStream outputStream
;
455 /** InputStreams obtained by the user. Not null indicates that the
456 * user has obtained the stream.
458 private InputStream returnedErrorStream
;
459 private InputStream returnedInputStream
;
462 * Lock Object for all processManager related locking.
464 private static Object queueLock
= new Object();
465 private static ProcessManager processManager
;
467 static class EOFInputStream
extends InputStream
469 static EOFInputStream instance
= new EOFInputStream();