mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			471 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			471 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Java
		
	
	
	
// 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;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 |