re PR libgcj/11801 (Problems with Process.waitFor() and exitValue())
2004-08-12 David Daney <ddaney@avtrex.com> PR libgcj/11801 * java/lang/PosixProcess.java: Rewrote. * java/lang/natPosixProcess.cc: Rewrote. * java/lang/Runtime.java (execInternal): Declare throws IOException. * gcj/javaprims.h (ConcreteProcess$ProcessManager): Declare. * posix-threads.cc (block_sigchld) New function. (_Jv_ThreadRegister) Use it. (_Jv_ThreadStart) Use it. * configure.in (PLATFORM_INNER_NAT_HDRS): New AC_SUBST() used in... * Makefile.am: ... to specify extra native headers. * configure: Regenerated. * include/config.h: Regenerated. * Makefile.in: Regenerated. * gcj/Makefile.in: Regenerated. * include/Makefile.in: Regenerated. * testsuite/Makefile.in: Regenerated. From-SVN: r85880
This commit is contained in:
@@ -21,12 +21,16 @@ details. */
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <gcj/cni.h>
|
||||
#include <jvm.h>
|
||||
|
||||
#include <java/lang/ConcreteProcess$ProcessManager.h>
|
||||
#include <java/lang/ConcreteProcess.h>
|
||||
#include <java/lang/IllegalThreadStateException.h>
|
||||
#include <java/lang/InternalError.h>
|
||||
#include <java/lang/InterruptedException.h>
|
||||
#include <java/lang/NullPointerException.h>
|
||||
#include <java/lang/Thread.h>
|
||||
@@ -42,44 +46,6 @@ using gnu::java::nio::channels::FileChannelImpl;
|
||||
|
||||
extern char **environ;
|
||||
|
||||
void
|
||||
java::lang::ConcreteProcess::destroy (void)
|
||||
{
|
||||
if (! hasExited)
|
||||
{
|
||||
// Really kill it.
|
||||
kill ((pid_t) pid, SIGKILL);
|
||||
}
|
||||
}
|
||||
|
||||
jint
|
||||
java::lang::ConcreteProcess::waitFor (void)
|
||||
{
|
||||
if (! hasExited)
|
||||
{
|
||||
int wstat;
|
||||
int r = waitpid ((pid_t) pid, &wstat, 0);
|
||||
|
||||
if (r == -1)
|
||||
{
|
||||
if (java::lang::Thread::interrupted())
|
||||
throw new InterruptedException (JvNewStringLatin1 (strerror
|
||||
(errno)));
|
||||
}
|
||||
else
|
||||
{
|
||||
hasExited = true;
|
||||
|
||||
if (WIFEXITED (wstat))
|
||||
status = WEXITSTATUS (wstat);
|
||||
else
|
||||
status = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static char *
|
||||
new_string (jstring string)
|
||||
{
|
||||
@@ -120,15 +86,135 @@ myclose (int &fd)
|
||||
fd = -1;
|
||||
}
|
||||
|
||||
// There has to be a signal handler in order to be able to
|
||||
// sigwait() on SIGCHLD. The information passed is ignored as it
|
||||
// will be recovered by the waitpid() call.
|
||||
static void
|
||||
sigchld_handler (int)
|
||||
{
|
||||
// Ignore.
|
||||
}
|
||||
|
||||
|
||||
// Get ready to enter the main reaper thread loop.
|
||||
void
|
||||
java::lang::ConcreteProcess::startProcess (jstringArray progarray,
|
||||
jstringArray envp,
|
||||
java::io::File *dir)
|
||||
java::lang::ConcreteProcess$ProcessManager::init ()
|
||||
{
|
||||
using namespace java::lang;
|
||||
// Remenber our PID so other threads can kill us.
|
||||
reaperPID = (jlong) pthread_self ();
|
||||
|
||||
// SIGCHLD is blocked in all threads in posix-threads.cc.
|
||||
// Setup the SIGCHLD handler.
|
||||
struct sigaction sa;
|
||||
memset (&sa, 0, sizeof (sa));
|
||||
|
||||
sa.sa_handler = sigchld_handler;
|
||||
// We only want signals when the things exit.
|
||||
sa.sa_flags = SA_NOCLDSTOP;
|
||||
|
||||
if (-1 == sigaction (SIGCHLD, &sa, NULL))
|
||||
goto error;
|
||||
|
||||
// All OK.
|
||||
return;
|
||||
|
||||
error:
|
||||
throw new InternalError (JvNewStringUTF (strerror (errno)));
|
||||
}
|
||||
|
||||
void
|
||||
java::lang::ConcreteProcess$ProcessManager::waitForSignal ()
|
||||
{
|
||||
using namespace java::lang;
|
||||
|
||||
sigset_t mask;
|
||||
// Wait for SIGCHLD
|
||||
sigemptyset (&mask);
|
||||
sigaddset (&mask, SIGCHLD);
|
||||
|
||||
int sig;
|
||||
int c = sigwait (&mask, &sig);
|
||||
|
||||
if (c != 0)
|
||||
goto error;
|
||||
|
||||
// All OK.
|
||||
return;
|
||||
|
||||
error:
|
||||
throw new InternalError (JvNewStringUTF (strerror (c)));
|
||||
}
|
||||
|
||||
jboolean java::lang::ConcreteProcess$ProcessManager::reap ()
|
||||
{
|
||||
using namespace java::lang;
|
||||
|
||||
pid_t pid;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
// Get the return code from a dead child process.
|
||||
int status;
|
||||
pid = waitpid ((pid_t) - 1, &status, WNOHANG);
|
||||
if (pid == -1)
|
||||
{
|
||||
if (errno == ECHILD)
|
||||
return false;
|
||||
else
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (pid == 0)
|
||||
return true; // No children to wait for.
|
||||
|
||||
// Look up the process in our pid map.
|
||||
ConcreteProcess * process = removeProcessFromMap ((jlong) pid);
|
||||
|
||||
if (process)
|
||||
{
|
||||
JvSynchronize sync (process);
|
||||
process->status = WIFEXITED (status) ? WEXITSTATUS (status) : -1;
|
||||
process->state = ConcreteProcess::STATE_TERMINATED;
|
||||
process->processTerminationCleanup();
|
||||
process->notifyAll ();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unknown child. How did this happen?
|
||||
fprintf (stderr, "Reaped unknown child pid = %ld\n", (long) pid);
|
||||
}
|
||||
}
|
||||
|
||||
error:
|
||||
throw new InternalError (JvNewStringUTF (strerror (errno)));
|
||||
}
|
||||
|
||||
void
|
||||
java::lang::ConcreteProcess$ProcessManager::signalReaper ()
|
||||
{
|
||||
int c = pthread_kill ((pthread_t) reaperPID, SIGCHLD);
|
||||
if (c == 0)
|
||||
return;
|
||||
// pthread_kill() failed.
|
||||
throw new InternalError (JvNewStringUTF (strerror (c)));
|
||||
}
|
||||
|
||||
void
|
||||
java::lang::ConcreteProcess::nativeDestroy ()
|
||||
{
|
||||
int c = kill ((pid_t) pid, SIGKILL);
|
||||
if (c == 0)
|
||||
return;
|
||||
// kill() failed.
|
||||
throw new InternalError (JvNewStringUTF (strerror (errno)));
|
||||
}
|
||||
|
||||
void
|
||||
java::lang::ConcreteProcess::nativeSpawn ()
|
||||
{
|
||||
using namespace java::io;
|
||||
|
||||
hasExited = false;
|
||||
|
||||
// Initialize all locals here to make cleanup simpler.
|
||||
char **args = NULL;
|
||||
char **env = NULL;
|
||||
@@ -142,7 +228,6 @@ java::lang::ConcreteProcess::startProcess (jstringArray progarray,
|
||||
errp[1] = -1;
|
||||
msgp[0] = -1;
|
||||
msgp[1] = -1;
|
||||
java::lang::Throwable *exc = NULL;
|
||||
errorStream = NULL;
|
||||
inputStream = NULL;
|
||||
outputStream = NULL;
|
||||
@@ -150,8 +235,7 @@ java::lang::ConcreteProcess::startProcess (jstringArray progarray,
|
||||
try
|
||||
{
|
||||
// Transform arrays to native form.
|
||||
args = (char **) _Jv_Malloc ((progarray->length + 1)
|
||||
* sizeof (char *));
|
||||
args = (char **) _Jv_Malloc ((progarray->length + 1) * sizeof (char *));
|
||||
|
||||
// Initialize so we can gracefully recover.
|
||||
jstring *elts = elements (progarray);
|
||||
@@ -185,24 +269,32 @@ java::lang::ConcreteProcess::startProcess (jstringArray progarray,
|
||||
// status.
|
||||
if (pipe (inp) || pipe (outp) || pipe (errp) || pipe (msgp)
|
||||
|| fcntl (msgp[1], F_SETFD, FD_CLOEXEC))
|
||||
throw new IOException (JvNewStringLatin1 (strerror (errno)));
|
||||
throw new IOException (JvNewStringUTF (strerror (errno)));
|
||||
|
||||
// We create the streams before forking. Otherwise if we had an
|
||||
// error while creating the streams we would have run the child
|
||||
// with no way to communicate with it.
|
||||
errorStream = new FileInputStream (new FileChannelImpl(errp[0], FileChannelImpl::READ));
|
||||
inputStream = new FileInputStream (new FileChannelImpl(inp[0], FileChannelImpl::READ));
|
||||
outputStream = new FileOutputStream (new FileChannelImpl(outp[1], FileChannelImpl::WRITE));
|
||||
errorStream =
|
||||
new FileInputStream (new
|
||||
FileChannelImpl (errp[0], FileChannelImpl::READ));
|
||||
inputStream =
|
||||
new FileInputStream (new
|
||||
FileChannelImpl (inp[0], FileChannelImpl::READ));
|
||||
outputStream =
|
||||
new FileOutputStream (new FileChannelImpl (outp[1],
|
||||
FileChannelImpl::WRITE));
|
||||
|
||||
// We don't use vfork() because that would cause the local
|
||||
// environment to be set by the child.
|
||||
if ((pid = (jlong) fork ()) == -1)
|
||||
throw new IOException (JvNewStringLatin1 (strerror (errno)));
|
||||
|
||||
if (pid == 0)
|
||||
// Use temporary for fork result to avoid dirtying an extra page.
|
||||
pid_t pid_tmp;
|
||||
if ((pid_tmp = fork ()) == -1)
|
||||
throw new IOException (JvNewStringUTF (strerror (errno)));
|
||||
|
||||
if (pid_tmp == 0)
|
||||
{
|
||||
// Child process, so remap descriptors, chdir and exec.
|
||||
|
||||
if (envp)
|
||||
{
|
||||
// Preserve PATH and LD_LIBRARY_PATH unless specified
|
||||
@@ -212,16 +304,16 @@ java::lang::ConcreteProcess::startProcess (jstringArray progarray,
|
||||
environ = env;
|
||||
if (path_val && getenv ("PATH") == NULL)
|
||||
{
|
||||
char *path_env = (char *) _Jv_Malloc (strlen (path_val)
|
||||
+ 5 + 1);
|
||||
char *path_env =
|
||||
(char *) _Jv_Malloc (strlen (path_val) + 5 + 1);
|
||||
strcpy (path_env, "PATH=");
|
||||
strcat (path_env, path_val);
|
||||
putenv (path_env);
|
||||
}
|
||||
if (ld_path_val && getenv ("LD_LIBRARY_PATH") == NULL)
|
||||
{
|
||||
char *ld_path_env
|
||||
= (char *) _Jv_Malloc (strlen (ld_path_val) + 16 + 1);
|
||||
char *ld_path_env =
|
||||
(char *) _Jv_Malloc (strlen (ld_path_val) + 16 + 1);
|
||||
strcpy (ld_path_env, "LD_LIBRARY_PATH=");
|
||||
strcat (ld_path_env, ld_path_val);
|
||||
putenv (ld_path_env);
|
||||
@@ -264,6 +356,8 @@ java::lang::ConcreteProcess::startProcess (jstringArray progarray,
|
||||
|
||||
// Parent. Close extra file descriptors and mark ours as
|
||||
// close-on-exec.
|
||||
pid = (jlong) pid_tmp;
|
||||
|
||||
myclose (outp[0]);
|
||||
myclose (inp[1]);
|
||||
myclose (errp[1]);
|
||||
@@ -272,9 +366,9 @@ java::lang::ConcreteProcess::startProcess (jstringArray progarray,
|
||||
char c;
|
||||
int r = read (msgp[0], &c, 1);
|
||||
if (r == -1)
|
||||
throw new IOException (JvNewStringLatin1 (strerror (errno)));
|
||||
throw new IOException (JvNewStringUTF (strerror (errno)));
|
||||
else if (r != 0)
|
||||
throw new IOException (JvNewStringLatin1 (strerror (c)));
|
||||
throw new IOException (JvNewStringUTF (strerror (c)));
|
||||
}
|
||||
catch (java::lang::Throwable *thrown)
|
||||
{
|
||||
@@ -324,15 +418,13 @@ java::lang::ConcreteProcess::startProcess (jstringArray progarray,
|
||||
myclose (errp[1]);
|
||||
myclose (msgp[1]);
|
||||
|
||||
exc = thrown;
|
||||
exception = thrown;
|
||||
}
|
||||
|
||||
myclose (msgp[0]);
|
||||
cleanup (args, env, path);
|
||||
|
||||
if (exc != NULL)
|
||||
throw exc;
|
||||
else
|
||||
if (exception == NULL)
|
||||
{
|
||||
fcntl (outp[1], F_SETFD, FD_CLOEXEC);
|
||||
fcntl (inp[0], F_SETFD, FD_CLOEXEC);
|
||||
|
||||
Reference in New Issue
Block a user