2016-05-14 Fritz Reese <fritzoreese@gmail.com>
[official-gcc.git] / libjava / java / lang / PosixProcess.java
blobdd59e7b93a11408a87110776daa1d3ef9694e341
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
8 details. */
10 package java.lang;
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;
19 import gnu.gcj.RawDataManaged;
21 /**
22 * @author Tom Tromey <tromey@cygnus.com>
23 * @date May 3, 1999
24 * @author David Daney <ddaney@avtrex.com> Rewrote using
25 * ProcessManager
27 final class PosixProcess extends Process
29 static final class ProcessManager extends Thread
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.
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;
44 ProcessManager()
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);
56 /**
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)
64 liveProcesses.add(p);
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.
72 * @param p The PosixProcess.
74 void startExecuting(PosixProcess p)
76 synchronized (queueLock)
78 queue.add(p);
79 signalReaper(); // If blocked in waitForSignal().
80 queueLock.notifyAll(); // If blocked in wait();
84 /**
85 * Block until the ProcessManager thread is ready to accept
86 * commands.
88 void waitUntilReady()
90 synchronized (this)
92 try
94 while (! ready)
95 wait();
97 catch (InterruptedException ie)
99 // Ignore.
105 * Main Process starting/reaping loop.
107 public void run()
109 init();
110 // Now ready to accept requests.
111 synchronized (this)
113 ready = true;
114 this.notifyAll();
117 for (;;)
121 synchronized (queueLock)
123 Iterator<PosixProcess> processIterator =
124 liveProcesses.iterator();
125 while (processIterator.hasNext())
127 boolean reaped = reap(processIterator.next());
128 if (reaped)
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.
148 return;
151 while (queue.size() > 0)
153 PosixProcess p = queue.remove(0);
154 p.spawn(this);
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();
164 catch (Exception ex)
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)
208 synchronized (this)
210 // If there is no ProcessManager we cannot kill.
211 if (state != STATE_TERMINATED)
213 if (processManager == null)
214 throw new InternalError();
215 nativeDestroy();
221 private native void nativeDestroy();
223 public int exitValue()
225 synchronized (this)
227 if (state != STATE_TERMINATED)
228 throw new IllegalThreadStateException("Process has not exited");
230 return status;
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
246 * EOFInputStreams.
248 void processTerminationCleanup()
252 outputStream.close();
254 catch (IOException ioe)
256 // Ignore.
260 if (returnedErrorStream == null && errorStream.available() == 0)
262 errorStream.close();
263 errorStream = null;
266 catch (IOException ioe)
268 // Ignore.
272 if (returnedInputStream == null && inputStream.available() == 0)
274 inputStream.close();
275 inputStream = null;
278 catch (IOException ioe)
280 // Ignore.
284 public synchronized InputStream getErrorStream()
286 if (returnedErrorStream != null)
287 return returnedErrorStream;
289 if (errorStream == null)
290 returnedErrorStream = EOFInputStream.instance;
291 else
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;
304 else
305 returnedInputStream = inputStream;
307 return returnedInputStream;
310 public OutputStream getOutputStream()
312 return outputStream;
315 public int waitFor() throws InterruptedException
317 synchronized (this)
319 while (state != STATE_TERMINATED)
320 wait();
322 return status;
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)
333 synchronized (this)
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();
348 * Do the fork and exec.
350 private native void nativeSpawn();
352 PosixProcess(String[] progarray, String[] envp, File dir, boolean redirect)
353 throws IOException
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;
361 this.envp = envp;
362 this.dir = dir;
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.
380 synchronized (this)
382 while (state == STATE_WAITING_TO_START)
386 wait();
388 catch (InterruptedException ie)
390 // FIXME: What to do when interrupted while blocking in a constructor?
391 // Ignore.
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);
403 throw ioe;
406 // Not an IOException. Something bad happened.
407 InternalError ie = new InternalError(exception.toString());
408 ie.initCause(exception);
409 throw ie;
412 // If we get here, all is well, the Process has started.
415 private String[] progarray;
416 private String[] envp;
417 private File dir;
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. */
424 long pid;
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. */
440 int state;
442 /** The exit status, if the child has exited. */
443 int status;
445 /** The streams. */
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();
465 public int read()
467 return -1;