
Imported GNU Classpath 0.90 * scripts/makemake.tcl: LocaleData.java moved to gnu/java/locale. * sources.am: Regenerated. * gcj/javaprims.h: Regenerated. * Makefile.in: Regenerated. * gcj/Makefile.in: Regenerated. * include/Makefile.in: Regenerated. * testsuite/Makefile.in: Regenerated. * gnu/java/lang/VMInstrumentationImpl.java: New override. * gnu/java/net/local/LocalSocketImpl.java: Likewise. * gnu/classpath/jdwp/VMMethod.java: Likewise. * gnu/classpath/jdwp/VMVirtualMachine.java: Update to latest interface. * java/lang/Thread.java: Add UncaughtExceptionHandler. * java/lang/reflect/Method.java: Implements GenericDeclaration and isSynthetic(), * java/lang/reflect/Field.java: Likewise. * java/lang/reflect/Constructor.java * java/lang/Class.java: Implements Type, GenericDeclaration, getSimpleName() and getEnclosing*() methods. * java/lang/Class.h: Add new public methods. * java/lang/Math.java: Add signum(), ulp() and log10(). * java/lang/natMath.cc (log10): New function. * java/security/VMSecureRandom.java: New override. * java/util/logging/Logger.java: Updated to latest classpath version. * java/util/logging/LogManager.java: New override. From-SVN: r113887
402 lines
11 KiB
Java
402 lines
11 KiB
Java
/* 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.Iterator;
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
/**
|
|
* 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()} 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(String)
|
|
*/
|
|
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
|
|
boolean redirect; // redirect stderr -> stdout
|
|
|
|
//
|
|
// 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.redirect);
|
|
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, boolean redirect)
|
|
throws IOException
|
|
{
|
|
|
|
// Initialize this process
|
|
this.state = INITIAL;
|
|
this.cmd = cmd;
|
|
this.env = env;
|
|
this.dir = dir;
|
|
this.redirect = redirect;
|
|
|
|
// 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;
|
|
if (stderr == null)
|
|
this.stderr = new InputStream()
|
|
{
|
|
public int read() throws IOException
|
|
{
|
|
return -1;
|
|
}
|
|
};
|
|
else
|
|
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, false);
|
|
}
|
|
|
|
static Process exec(List cmd, Map env,
|
|
File dir, boolean redirect) throws IOException
|
|
{
|
|
String[] acmd = (String[]) cmd.toArray(new String[cmd.size()]);
|
|
String[] aenv = new String[env.size()];
|
|
|
|
int i = 0;
|
|
Iterator iter = env.entrySet().iterator();
|
|
while (iter.hasNext())
|
|
{
|
|
Map.Entry entry = (Map.Entry) iter.next();
|
|
aenv[i++] = entry.getKey() + "=" + entry.getValue();
|
|
}
|
|
|
|
return new VMProcess(acmd, aenv, dir, redirect);
|
|
}
|
|
|
|
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,
|
|
boolean redirect)
|
|
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);
|
|
}
|