Initial revision
From-SVN: r102075
This commit is contained in:
parent
f911ba985a
commit
37d375fdc5
32 changed files with 5340 additions and 0 deletions
368
libjava/classpath/vm/reference/java/lang/VMProcess.java
Normal file
368
libjava/classpath/vm/reference/java/lang/VMProcess.java
Normal file
|
@ -0,0 +1,368 @@
|
|||
/* java.lang.VMProcess -- VM implementation of java.lang.Process
|
||||
Copyright (C) 2004, 2005 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
package java.lang;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* Represents one external process. Each instance of this class is in
|
||||
* one of three states: INITIAL, RUNNING, or TERMINATED. The instance
|
||||
* is {@link Object#notifyAll notifyAll()}'d each time the state changes.
|
||||
* The state of all instances is managed by a single dedicated thread
|
||||
* which does the actual fork()/exec() and wait() system calls. User
|
||||
* threads {@link Object#wait wait()} on the instance when creating the
|
||||
* process or waiting for it to terminate.
|
||||
*
|
||||
* <p>
|
||||
* See
|
||||
* <a href="http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11801">GCC bug
|
||||
* #11801</a> for the motivation behind the design of this class.
|
||||
*
|
||||
* @author Archie Cobbs
|
||||
* @see Process
|
||||
* @see Runtime#exec
|
||||
*/
|
||||
final class VMProcess extends Process
|
||||
{
|
||||
|
||||
// Possible states for a VMProcess
|
||||
private static final int INITIAL = 0;
|
||||
private static final int RUNNING = 1;
|
||||
private static final int TERMINATED = 2;
|
||||
|
||||
// Dedicated thread that does all the fork()'ing and wait()'ing.
|
||||
static Thread processThread;
|
||||
|
||||
// New processes waiting to be spawned by processThread.
|
||||
static final LinkedList workList = new LinkedList();
|
||||
|
||||
// Return values set by nativeReap() when a child is reaped.
|
||||
// These are only accessed by processThread so no locking required.
|
||||
static long reapedPid;
|
||||
static int reapedExitValue;
|
||||
|
||||
// Information about this process
|
||||
int state; // current state of process
|
||||
final String[] cmd; // copied from Runtime.exec()
|
||||
final String[] env; // copied from Runtime.exec()
|
||||
final File dir; // copied from Runtime.exec()
|
||||
Throwable exception; // if process failed to start
|
||||
long pid; // process id
|
||||
OutputStream stdin; // process input stream
|
||||
InputStream stdout; // process output stream
|
||||
InputStream stderr; // process error stream
|
||||
int exitValue; // process exit value
|
||||
|
||||
//
|
||||
// Dedicated thread that does all the fork()'ing and wait()'ing
|
||||
// for external processes. This is needed because some systems like
|
||||
// Linux use a process-per-thread model, which means the same thread
|
||||
// that did the fork()/exec() must also do the wait().
|
||||
//
|
||||
private static class ProcessThread extends Thread
|
||||
{
|
||||
|
||||
// Max time (in ms) we'll delay before trying to reap another child.
|
||||
private static final int MAX_REAP_DELAY = 1000;
|
||||
|
||||
// Processes created but not yet terminated; maps Long(pid) -> VMProcess
|
||||
// Only used in run() and spawn() method from this Thread, so no locking.
|
||||
private final HashMap activeMap = new HashMap();
|
||||
|
||||
// We have an explicit constructor, because the default
|
||||
// constructor will be private, which means the compiler will have
|
||||
// to generate a second package-private constructor, which is
|
||||
// bogus.
|
||||
ProcessThread ()
|
||||
{
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
final LinkedList workList = VMProcess.workList;
|
||||
while (true)
|
||||
{
|
||||
|
||||
// Get the next process to spawn (if any) and spawn it. Spawn
|
||||
// at most one at a time before checking for reapable children.
|
||||
VMProcess process = null;
|
||||
synchronized (workList)
|
||||
{
|
||||
if (!workList.isEmpty())
|
||||
process = (VMProcess)workList.removeFirst();
|
||||
}
|
||||
|
||||
if (process != null)
|
||||
spawn(process);
|
||||
|
||||
|
||||
// Check for termination of active child processes
|
||||
while (!activeMap.isEmpty() && VMProcess.nativeReap())
|
||||
{
|
||||
long pid = VMProcess.reapedPid;
|
||||
int exitValue = VMProcess.reapedExitValue;
|
||||
process = (VMProcess)activeMap.remove(new Long(pid));
|
||||
if (process != null)
|
||||
{
|
||||
synchronized (process)
|
||||
{
|
||||
process.exitValue = exitValue;
|
||||
process.state = TERMINATED;
|
||||
process.notify();
|
||||
}
|
||||
}
|
||||
else
|
||||
System.err.println("VMProcess WARNING reaped unknown process: "
|
||||
+ pid);
|
||||
}
|
||||
|
||||
|
||||
// If there are more new processes to create, go do that now.
|
||||
// If there is nothing left to do, exit this thread. Otherwise,
|
||||
// sleep a little while, and then check again for reapable children.
|
||||
// We will get woken up immediately if there are new processes to
|
||||
// spawn, but not if there are new children to reap. So we only
|
||||
// sleep a short time, in effect polling while processes are active.
|
||||
synchronized (workList)
|
||||
{
|
||||
if (!workList.isEmpty())
|
||||
continue;
|
||||
if (activeMap.isEmpty())
|
||||
{
|
||||
processThread = null;
|
||||
break;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
workList.wait(MAX_REAP_DELAY);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Spawn a process
|
||||
private void spawn(VMProcess process)
|
||||
{
|
||||
|
||||
// Spawn the process and put it in our active map indexed by pid.
|
||||
// If the spawn operation fails, store the exception with the process.
|
||||
// In either case, wake up thread that created the process.
|
||||
synchronized (process)
|
||||
{
|
||||
try
|
||||
{
|
||||
process.nativeSpawn(process.cmd, process.env, process.dir);
|
||||
process.state = RUNNING;
|
||||
activeMap.put(new Long(process.pid), process);
|
||||
}
|
||||
catch (ThreadDeath death)
|
||||
{
|
||||
throw death;
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
process.state = TERMINATED;
|
||||
process.exception = t;
|
||||
}
|
||||
process.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Constructor
|
||||
private VMProcess(String[] cmd, String[] env, File dir) throws IOException
|
||||
{
|
||||
|
||||
// Initialize this process
|
||||
this.state = INITIAL;
|
||||
this.cmd = cmd;
|
||||
this.env = env;
|
||||
this.dir = dir;
|
||||
|
||||
// Add process to the new process work list and wakeup processThread
|
||||
synchronized (workList)
|
||||
{
|
||||
workList.add(this);
|
||||
if (processThread == null)
|
||||
{
|
||||
processThread = new ProcessThread();
|
||||
processThread.setDaemon(true);
|
||||
processThread.start();
|
||||
}
|
||||
else
|
||||
{
|
||||
workList.notify();
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for processThread to spawn this process and update its state
|
||||
synchronized (this)
|
||||
{
|
||||
while (state == INITIAL)
|
||||
{
|
||||
try
|
||||
{
|
||||
wait();
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If spawning failed, rethrow the exception in this thread
|
||||
if (exception != null)
|
||||
{
|
||||
exception.fillInStackTrace();
|
||||
if (exception instanceof IOException)
|
||||
throw (IOException)exception;
|
||||
|
||||
if (exception instanceof Error)
|
||||
throw (Error)exception;
|
||||
|
||||
if (exception instanceof RuntimeException)
|
||||
throw (RuntimeException)exception;
|
||||
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
// Invoked by native code (from nativeSpawn()) to record process info.
|
||||
private void setProcessInfo(OutputStream stdin,
|
||||
InputStream stdout, InputStream stderr, long pid)
|
||||
{
|
||||
this.stdin = stdin;
|
||||
this.stdout = stdout;
|
||||
this.stderr = stderr;
|
||||
this.pid = pid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point from Runtime.exec().
|
||||
*/
|
||||
static Process exec(String[] cmd, String[] env, File dir) throws IOException
|
||||
{
|
||||
return new VMProcess(cmd, env, dir);
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream()
|
||||
{
|
||||
return stdin;
|
||||
}
|
||||
|
||||
public InputStream getInputStream()
|
||||
{
|
||||
return stdout;
|
||||
}
|
||||
|
||||
public InputStream getErrorStream()
|
||||
{
|
||||
return stderr;
|
||||
}
|
||||
|
||||
public synchronized int waitFor() throws InterruptedException
|
||||
{
|
||||
while (state != TERMINATED)
|
||||
wait();
|
||||
return exitValue;
|
||||
}
|
||||
|
||||
public synchronized int exitValue()
|
||||
{
|
||||
if (state != TERMINATED)
|
||||
throw new IllegalThreadStateException();
|
||||
return exitValue;
|
||||
}
|
||||
|
||||
public synchronized void destroy()
|
||||
{
|
||||
if (state == TERMINATED)
|
||||
return;
|
||||
|
||||
nativeKill(pid);
|
||||
|
||||
while (state != TERMINATED)
|
||||
{
|
||||
try
|
||||
{
|
||||
wait();
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the fork()/exec() thing to create the O/S process.
|
||||
* Must invoke setProcessInfo() before returning successfully.
|
||||
* This method is only invoked by processThread.
|
||||
*
|
||||
* @throws IOException if the O/S process could not be created.
|
||||
*/
|
||||
native void nativeSpawn(String[] cmd, String[] env, File dir)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Test for a reapable child process, and reap if so. Does not block.
|
||||
* If a child was reaped, this method must set reapedPid and
|
||||
* reapedExitValue appropriately before returning.
|
||||
* This method is only invoked by processThread.
|
||||
*
|
||||
* @return true if a child was reaped, otherwise false
|
||||
*/
|
||||
// This is not private as it is called from an inner class.
|
||||
static native boolean nativeReap();
|
||||
|
||||
/**
|
||||
* Kill a process. This sends it a fatal signal but does not reap it.
|
||||
*/
|
||||
private static native void nativeKill(long pid);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue