Save This Page
Home » openjdk-7 » java » lang » [javadoc | source]
    1   /* java.lang.VMProcess -- VM implementation of java.lang.Process
    2      Copyright (C) 2004, 2005 Free Software Foundation, Inc.
    3   
    4   This file is part of GNU Classpath.
    5   
    6   GNU Classpath is free software; you can redistribute it and/or modify
    7   it under the terms of the GNU General Public License as published by
    8   the Free Software Foundation; either version 2, or (at your option)
    9   any later version.
   10   
   11   GNU Classpath is distributed in the hope that it will be useful, but
   12   WITHOUT ANY WARRANTY; without even the implied warranty of
   13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   14   General Public License for more details.
   15   
   16   You should have received a copy of the GNU General Public License
   17   along with GNU Classpath; see the file COPYING.  If not, write to the
   18   Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
   19   02110-1301 USA.
   20   
   21   Linking this library statically or dynamically with other modules is
   22   making a combined work based on this library.  Thus, the terms and
   23   conditions of the GNU General Public License cover the whole
   24   combination.
   25   
   26   As a special exception, the copyright holders of this library give you
   27   permission to link this library with independent modules to produce an
   28   executable, regardless of the license terms of these independent
   29   modules, and to copy and distribute the resulting executable under
   30   terms of your choice, provided that you also meet, for each linked
   31   independent module, the terms and conditions of the license of that
   32   module.  An independent module is a module which is not derived from
   33   or based on this library.  If you modify this library, you may extend
   34   this exception to your version of the library, but you are not
   35   obligated to do so.  If you do not wish to do so, delete this
   36   exception statement from your version. */
   37   
   38   package java.lang;
   39   
   40   import java.io.File;
   41   import java.io.IOException;
   42   import java.io.InputStream;
   43   import java.io.OutputStream;
   44   import java.util.HashMap;
   45   import java.util.Iterator;
   46   import java.util.LinkedList;
   47   import java.util.List;
   48   import java.util.Map;
   49   
   50   /**
   51    * Represents one external process. Each instance of this class is in
   52    * one of three states: INITIAL, RUNNING, or TERMINATED. The instance
   53    * is {@link Object#notifyAll notifyAll()}'d each time the state changes.
   54    * The state of all instances is managed by a single dedicated thread
   55    * which does the actual fork()/exec() and wait() system calls. User
   56    * threads {@link Object#wait()} on the instance when creating the
   57    * process or waiting for it to terminate.
   58    *
   59    * <p>
   60    * See
   61    * <a href="http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11801">GCC bug
   62    * #11801</a> for the motivation behind the design of this class.
   63    *
   64    * @author Archie Cobbs
   65    * @see Process
   66    * @see Runtime#exec(String)
   67    */
   68   final class VMProcess extends Process
   69   {
   70   
   71     // Possible states for a VMProcess
   72     private static final int INITIAL = 0;
   73     private static final int RUNNING = 1;
   74     private static final int TERMINATED = 2;
   75   
   76     // Dedicated thread that does all the fork()'ing and wait()'ing.
   77     static Thread processThread;
   78   
   79     // New processes waiting to be spawned by processThread.
   80     static final LinkedList workList = new LinkedList();
   81   
   82     // Return values set by nativeReap() when a child is reaped.
   83     // These are only accessed by processThread so no locking required.
   84     static long reapedPid;
   85     static int reapedExitValue;
   86   
   87     // Information about this process
   88     int state;				       // current state of process
   89     final String[] cmd;			       // copied from Runtime.exec()
   90     final String[] env;			       // copied from Runtime.exec()
   91     final File dir;			       // copied from Runtime.exec()
   92     Throwable exception;			       // if process failed to start
   93     long pid;				       // process id
   94     OutputStream stdin;			       // process input stream
   95     InputStream stdout;			       // process output stream
   96     InputStream stderr;			       // process error stream
   97     int exitValue;			       // process exit value
   98     boolean redirect;			       // redirect stderr -> stdout
   99   
  100     //
  101     // Dedicated thread that does all the fork()'ing and wait()'ing
  102     // for external processes. This is needed because some systems like
  103     // Linux use a process-per-thread model, which means the same thread
  104     // that did the fork()/exec() must also do the wait().
  105     //
  106     private static class ProcessThread extends Thread
  107     {
  108   
  109       // Max time (in ms) we'll delay before trying to reap another child.
  110       private static final int MAX_REAP_DELAY = 1000;
  111   
  112       // Processes created but not yet terminated; maps Long(pid) -> VMProcess
  113       // Only used in run() and spawn() method from this Thread, so no locking.
  114       private final HashMap activeMap = new HashMap();
  115   
  116       // We have an explicit constructor, because the default
  117       // constructor will be private, which means the compiler will have
  118       // to generate a second package-private constructor, which is
  119       // bogus.
  120       ProcessThread ()
  121       {
  122       }
  123   
  124       public void run()
  125       {
  126         final LinkedList workList = VMProcess.workList;
  127         while (true)
  128   	{
  129   
  130   	  // Get the next process to spawn (if any) and spawn it. Spawn
  131   	  // at most one at a time before checking for reapable children.
  132   	  VMProcess process = null;
  133   	  synchronized (workList)
  134   	    {
  135   	      if (!workList.isEmpty())
  136   		process = (VMProcess)workList.removeFirst();
  137   	    }
  138   
  139   	  if (process != null)
  140   	    spawn(process);
  141   
  142   
  143   	  // Check for termination of active child processes
  144   	  while (!activeMap.isEmpty() && VMProcess.nativeReap())
  145   	    {
  146   	      long pid = VMProcess.reapedPid;
  147   	      int exitValue = VMProcess.reapedExitValue;
  148   	      process = (VMProcess)activeMap.remove(new Long(pid));
  149   	      if (process != null)
  150   		{
  151   		  synchronized (process)
  152   		    {
  153   		      process.exitValue = exitValue;
  154   		      process.state = TERMINATED;
  155   		      process.notify();
  156   		    }
  157   		}
  158   	      else
  159   		System.err.println("VMProcess WARNING reaped unknown process: "
  160   				   + pid);
  161   	    }
  162   
  163   
  164   	  // If there are more new processes to create, go do that now.
  165   	  // If there is nothing left to do, exit this thread. Otherwise,
  166   	  // sleep a little while, and then check again for reapable children.
  167   	  // We will get woken up immediately if there are new processes to
  168   	  // spawn, but not if there are new children to reap. So we only
  169   	  // sleep a short time, in effect polling while processes are active.
  170   	  synchronized (workList)
  171   	    {
  172   	      if (!workList.isEmpty())
  173   		continue;
  174   	      if (activeMap.isEmpty())
  175   		{
  176   		  processThread = null;
  177   		  break;
  178   		}
  179   	
  180   	      try
  181   		{
  182   		  workList.wait(MAX_REAP_DELAY);
  183   		}
  184   	      catch (InterruptedException e)
  185   		{
  186   		  /* ignore */
  187   		}
  188   	    }
  189   	}
  190       }
  191   
  192       // Spawn a process
  193       private void spawn(VMProcess process)
  194       {
  195         
  196         // Spawn the process and put it in our active map indexed by pid.
  197         // If the spawn operation fails, store the exception with the process.
  198         // In either case, wake up thread that created the process.
  199         synchronized (process)
  200   	{
  201   	  try
  202   	    {
  203   	      process.nativeSpawn(process.cmd, process.env, process.dir,
  204   				  process.redirect);
  205   	      process.state = RUNNING;
  206   	      activeMap.put(new Long(process.pid), process);
  207   	    }
  208             catch (ThreadDeath death)
  209               {
  210                 throw death;
  211               }
  212   	  catch (Throwable t)
  213   	    {
  214   	      process.state = TERMINATED;
  215   	      process.exception = t;
  216   	    }
  217   	  process.notify();
  218   	}
  219       }
  220     }
  221   
  222     // Constructor
  223     private VMProcess(String[] cmd, String[] env, File dir, boolean redirect)
  224       throws IOException
  225     {
  226       
  227       // Initialize this process
  228       this.state = INITIAL;
  229       this.cmd = cmd;
  230       this.env = env;
  231       this.dir = dir;
  232       this.redirect = redirect;
  233     
  234       // Add process to the new process work list and wakeup processThread
  235       synchronized (workList)
  236         {
  237   	workList.add(this);
  238   	if (processThread == null)
  239   	  {
  240   	    processThread = new ProcessThread();
  241   	    processThread.setDaemon(true);
  242   	    processThread.start();
  243   	  }
  244   	else
  245   	  {
  246   	    workList.notify();
  247   	  }
  248         }
  249   
  250       // Wait for processThread to spawn this process and update its state
  251       synchronized (this)
  252         {
  253   	while (state == INITIAL)
  254   	  {
  255   	    try
  256   	      {
  257   		wait();
  258   	      }
  259   	    catch (InterruptedException e)
  260   	      {
  261   		/* ignore */
  262   	      }
  263   	  }
  264         }
  265   
  266       // If spawning failed, rethrow the exception in this thread
  267       if (exception != null)
  268         {
  269   	exception.fillInStackTrace();
  270   	if (exception instanceof IOException)
  271   	  throw (IOException)exception;
  272   
  273   	if (exception instanceof Error)
  274   	  throw (Error)exception;
  275   
  276   	if (exception instanceof RuntimeException)
  277   	  throw (RuntimeException)exception;
  278   
  279   	throw new RuntimeException(exception);
  280         }
  281     }
  282   
  283     // Invoked by native code (from nativeSpawn()) to record process info.
  284     private void setProcessInfo(OutputStream stdin,
  285   			      InputStream stdout, InputStream stderr, long pid)
  286     {
  287       this.stdin = stdin;
  288       this.stdout = stdout;
  289       if (stderr == null)
  290         this.stderr = new InputStream()
  291   	{
  292   	  public int read() throws IOException
  293   	  {
  294   	    return -1;
  295   	  }
  296   	};
  297       else
  298         this.stderr = stderr;
  299       this.pid = pid;
  300     }
  301   
  302     /**
  303      * Entry point from Runtime.exec().
  304      */
  305     static Process exec(String[] cmd, String[] env, File dir) throws IOException
  306     {
  307       return new VMProcess(cmd, env, dir, false);
  308     }
  309   
  310     static Process exec(List cmd, Map env,
  311   		      File dir, boolean redirect) throws IOException
  312     {
  313       String[] acmd = (String[]) cmd.toArray(new String[cmd.size()]);
  314       String[] aenv = new String[env.size()];
  315   
  316       int i = 0;
  317       Iterator iter = env.entrySet().iterator();
  318       while (iter.hasNext())
  319         {
  320   	Map.Entry entry = (Map.Entry) iter.next();
  321   	aenv[i++] = entry.getKey() + "=" + entry.getValue();
  322         }
  323   
  324       return new VMProcess(acmd, aenv, dir, redirect);
  325     }
  326   
  327     public OutputStream getOutputStream()
  328     {
  329       return stdin;
  330     }
  331   
  332     public InputStream getInputStream()
  333     {
  334       return stdout;
  335     }
  336   
  337     public InputStream getErrorStream()
  338     {
  339       return stderr;
  340     }
  341   
  342     public synchronized int waitFor() throws InterruptedException
  343     {
  344       while (state != TERMINATED)
  345         wait();
  346       return exitValue;
  347     }
  348   
  349     public synchronized int exitValue()
  350     {
  351       if (state != TERMINATED)
  352         throw new IllegalThreadStateException();
  353       return exitValue;
  354     }
  355   
  356     public synchronized void destroy()
  357     {
  358       if (state == TERMINATED)
  359         return;
  360   
  361       nativeKill(pid);
  362       
  363       while (state != TERMINATED)
  364         {
  365   	try
  366   	  {
  367   	    wait();
  368   	  }
  369   	catch (InterruptedException e)
  370   	  {
  371   	    /* ignore */
  372   	  }
  373         }
  374     }
  375   
  376     /**
  377      * Does the fork()/exec() thing to create the O/S process.
  378      * Must invoke setProcessInfo() before returning successfully.
  379      * This method is only invoked by processThread.
  380      *
  381      * @throws IOException if the O/S process could not be created.
  382      */
  383     native void nativeSpawn(String[] cmd, String[] env, File dir,
  384   			  boolean redirect)
  385       throws IOException;
  386   
  387     /**
  388      * Test for a reapable child process, and reap if so. Does not block.
  389      * If a child was reaped, this method must set reapedPid and
  390      * reapedExitValue appropriately before returning.
  391      * This method is only invoked by processThread.
  392      *
  393      * @return true if a child was reaped, otherwise false
  394      */
  395     // This is not private as it is called from an inner class.
  396     static native boolean nativeReap();
  397   
  398     /**
  399      * Kill a process. This sends it a fatal signal but does not reap it.
  400      */
  401     private static native void nativeKill(long pid);
  402   }

Save This Page
Home » openjdk-7 » java » lang » [javadoc | source]