diff options
author | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
---|---|---|
committer | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
commit | 554fd8c5195424bdbcabf5de30fdc183aba391bd (patch) | |
tree | 976dc5ab7fddf506dadce60ae936f43f58787092 /libjava/java/lang/PosixProcess.java | |
download | cbb-gcc-4.6.4-15d2061ac0796199866debe9ac87130894b0cdd3.tar.bz2 cbb-gcc-4.6.4-15d2061ac0796199866debe9ac87130894b0cdd3.tar.xz |
obtained gcc-4.6.4.tar.bz2 from upstream website;upstream
verified gcc-4.6.4.tar.bz2.sig;
imported gcc-4.6.4 source tree from verified upstream tarball.
downloading a git-generated archive based on the 'upstream' tag
should provide you with a source tree that is binary identical
to the one extracted from the above tarball.
if you have obtained the source via the command 'git clone',
however, do note that line-endings of files in your working
directory might differ from line-endings of the respective
files in the upstream repository.
Diffstat (limited to 'libjava/java/lang/PosixProcess.java')
-rw-r--r-- | libjava/java/lang/PosixProcess.java | 470 |
1 files changed, 470 insertions, 0 deletions
diff --git a/libjava/java/lang/PosixProcess.java b/libjava/java/lang/PosixProcess.java new file mode 100644 index 000000000..dd59e7b93 --- /dev/null +++ b/libjava/java/lang/PosixProcess.java @@ -0,0 +1,470 @@ +// PosixProcess.java - Subclass of Process for POSIX systems. +/* Copyright (C) 1998, 1999, 2004, 2006, 2007 Free Software Foundation + + This file is part of libgcj. + +This software is copyrighted work licensed under the terms of the +Libgcj License. Please consult the file "LIBGCJ_LICENSE" for +details. */ + +package java.lang; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Iterator; +import java.util.LinkedList; + +import gnu.gcj.RawDataManaged; + +/** + * @author Tom Tromey <tromey@cygnus.com> + * @date May 3, 1999 + * @author David Daney <ddaney@avtrex.com> Rewrote using + * ProcessManager + */ +final class PosixProcess extends Process +{ + static final class ProcessManager extends Thread + { + /** + * A list of {@link PosixProcess PosixProcesses} to be + * started. The queueLock object is used as the lock Object + * for all process related operations. To avoid dead lock + * ensure queueLock is obtained before PosixProcess. + */ + private LinkedList<PosixProcess> queue = new LinkedList<PosixProcess>(); + private LinkedList<PosixProcess> liveProcesses = + new LinkedList<PosixProcess>(); + private boolean ready = false; + + static RawDataManaged nativeData; + + ProcessManager() + { + // Use package private Thread constructor to place us in the + // root ThreadGroup with no InheritableThreadLocal. If the + // InheritableThreadLocals were allowed to initialize, they could + // cause a Runtime.exec() to be called causing infinite + // recursion. + super("ProcessManager", true); + // Don't keep the (main) process from exiting on our account. + this.setDaemon(true); + } + + /** + * Add a process to the list of running processes. This must only + * be called with the queueLock held. + * + * @param p The PosixProcess. + */ + void addToLiveProcesses(PosixProcess p) + { + liveProcesses.add(p); + } + + /** + * Queue up the PosixProcess and awake the ProcessManager. + * The ProcessManager will start the PosixProcess from its + * thread so it can be reaped when it terminates. + * + * @param p The PosixProcess. + */ + void startExecuting(PosixProcess p) + { + synchronized (queueLock) + { + queue.add(p); + signalReaper(); // If blocked in waitForSignal(). + queueLock.notifyAll(); // If blocked in wait(); + } + } + + /** + * Block until the ProcessManager thread is ready to accept + * commands. + */ + void waitUntilReady() + { + synchronized (this) + { + try + { + while (! ready) + wait(); + } + catch (InterruptedException ie) + { + // Ignore. + } + } + } + + /** + * Main Process starting/reaping loop. + */ + public void run() + { + init(); + // Now ready to accept requests. + synchronized (this) + { + ready = true; + this.notifyAll(); + } + + for (;;) + { + try + { + synchronized (queueLock) + { + Iterator<PosixProcess> processIterator = + liveProcesses.iterator(); + while (processIterator.hasNext()) + { + boolean reaped = reap(processIterator.next()); + if (reaped) + processIterator.remove(); + } + if (liveProcesses.size() == 0 && queue.size() == 0) + { + // This reaper thread could exit, but we keep it + // alive for a while in case someone wants to + // start more Processes. + try + { + queueLock.wait(1000L); + if (queue.size() == 0) + { + processManager = null; + return; // Timed out. + } + } + catch (InterruptedException ie) + { + // Ignore and exit the thread. + return; + } + } + while (queue.size() > 0) + { + PosixProcess p = queue.remove(0); + p.spawn(this); + } + } + + // Wait for a SIGCHLD from either an exiting process or + // the startExecuting() method. This is done outside of + // the synchronized block to allow other threads to + // enter and submit more jobs. + waitForSignal(); + } + catch (Exception ex) + { + ex.printStackTrace(System.err); + } + } + } + + /** + * Setup native signal handlers and other housekeeping things. + */ + private native void init(); + + /** + * Block waiting for SIGCHLD. + * + */ + private native void waitForSignal(); + + /** + * Try to reap the specified child without blocking. + * + * @param p the process to try to reap. + * + * @return true if the process terminated. + * + */ + private native boolean reap(PosixProcess p); + + /** + * Send SIGCHLD to the reaper thread. + */ + private native void signalReaper(); + } + + public void destroy() + { + // Synchronized on the queueLock. This ensures that the reaper + // thread cannot be doing a wait() on the child. + // Otherwise there would be a race where the OS could + // create a process with the same pid between the wait() + // and the update of the state which would cause a kill to + // the wrong process. + synchronized (queueLock) + { + synchronized (this) + { + // If there is no ProcessManager we cannot kill. + if (state != STATE_TERMINATED) + { + if (processManager == null) + throw new InternalError(); + nativeDestroy(); + } + } + } + } + + private native void nativeDestroy(); + + public int exitValue() + { + synchronized (this) + { + if (state != STATE_TERMINATED) + throw new IllegalThreadStateException("Process has not exited"); + } + return status; + } + + /** + * Called by native code when process exits. + * + * Already synchronized (this). Close any streams that we can to + * conserve file descriptors. + * + * The outputStream can be closed as any future writes will + * generate an IOException due to EPIPE. + * + * The inputStream and errorStream can only be closed if the user + * has not obtained a reference to them AND they have no bytes + * available. Since the process has terminated they will never have + * any more data available and can safely be replaced by + * EOFInputStreams. + */ + void processTerminationCleanup() + { + try + { + outputStream.close(); + } + catch (IOException ioe) + { + // Ignore. + } + try + { + if (returnedErrorStream == null && errorStream.available() == 0) + { + errorStream.close(); + errorStream = null; + } + } + catch (IOException ioe) + { + // Ignore. + } + try + { + if (returnedInputStream == null && inputStream.available() == 0) + { + inputStream.close(); + inputStream = null; + } + } + catch (IOException ioe) + { + // Ignore. + } + } + + public synchronized InputStream getErrorStream() + { + if (returnedErrorStream != null) + return returnedErrorStream; + + if (errorStream == null) + returnedErrorStream = EOFInputStream.instance; + else + returnedErrorStream = errorStream; + + return returnedErrorStream; + } + + public synchronized InputStream getInputStream() + { + if (returnedInputStream != null) + return returnedInputStream; + + if (inputStream == null) + returnedInputStream = EOFInputStream.instance; + else + returnedInputStream = inputStream; + + return returnedInputStream; + } + + public OutputStream getOutputStream() + { + return outputStream; + } + + public int waitFor() throws InterruptedException + { + synchronized (this) + { + while (state != STATE_TERMINATED) + wait(); + } + return status; + } + + /** + * Start this process running. This should only be called by the + * ProcessManager with the queueLock held. + * + * @param pm The ProcessManager that made the call. + */ + void spawn(ProcessManager pm) + { + synchronized (this) + { + // Do the fork/exec magic. + nativeSpawn(); + // There is no race with reap() in the pidToProcess map + // because this is always called from the same thread + // doing the reaping. + pm.addToLiveProcesses(this); + state = STATE_RUNNING; + // Notify anybody waiting on state change. + this.notifyAll(); + } + } + + /** + * Do the fork and exec. + */ + private native void nativeSpawn(); + + PosixProcess(String[] progarray, String[] envp, File dir, boolean redirect) + throws IOException + { + // Check to ensure there is something to run, and avoid + // dereferencing null pointers in native code. + if (progarray[0] == null) + throw new NullPointerException(); + + this.progarray = progarray; + this.envp = envp; + this.dir = dir; + this.redirect = redirect; + + // Start a ProcessManager if there is not one already running. + synchronized (queueLock) + { + if (processManager == null) + { + processManager = new ProcessManager(); + processManager.start(); + processManager.waitUntilReady(); + } + + // Queue this PosixProcess for starting by the ProcessManager. + processManager.startExecuting(this); + } + + // Wait until ProcessManager has started us. + synchronized (this) + { + while (state == STATE_WAITING_TO_START) + { + try + { + wait(); + } + catch (InterruptedException ie) + { + // FIXME: What to do when interrupted while blocking in a constructor? + // Ignore. + } + } + } + + // If there was a problem, re-throw it. + if (exception != null) + { + if (exception instanceof IOException) + { + IOException ioe = new IOException(exception.toString()); + ioe.initCause(exception); + throw ioe; + } + + // Not an IOException. Something bad happened. + InternalError ie = new InternalError(exception.toString()); + ie.initCause(exception); + throw ie; + } + + // If we get here, all is well, the Process has started. + } + + private String[] progarray; + private String[] envp; + private File dir; + private boolean redirect; + + /** Set by the ProcessManager on problems starting. */ + private Throwable exception; + + /** The process id. This is cast to a pid_t on the native side. */ + long pid; + + // FIXME: Why doesn't the friend declaration in PosixProcess.h + // allow PosixProcess$ProcessManager native code access these + // when they are private? + + /** Before the process is forked. */ + static final int STATE_WAITING_TO_START = 0; + + /** After the fork. */ + static final int STATE_RUNNING = 1; + + /** After exit code has been collected. */ + static final int STATE_TERMINATED = 2; + + /** One of STATE_WAITING_TO_START, STATE_RUNNING, STATE_TERMINATED. */ + int state; + + /** The exit status, if the child has exited. */ + int status; + + /** The streams. */ + private InputStream errorStream; + private InputStream inputStream; + private OutputStream outputStream; + + /** InputStreams obtained by the user. Not null indicates that the + * user has obtained the stream. + */ + private InputStream returnedErrorStream; + private InputStream returnedInputStream; + + /** + * Lock Object for all processManager related locking. + */ + private static Object queueLock = new Object(); + private static ProcessManager processManager; + + static class EOFInputStream extends InputStream + { + static EOFInputStream instance = new EOFInputStream(); + public int read() + { + return -1; + } + } +} |