diff options
| author | Jing Yu <jingyu@google.com> | 2009-11-05 15:11:04 -0800 |
|---|---|---|
| committer | Jing Yu <jingyu@google.com> | 2009-11-05 15:11:04 -0800 |
| commit | df62c1c110e8532b995b23540b7e3695729c0779 (patch) | |
| tree | dbbd4cbdb50ac38011e058a2533ee4c3168b0205 /gcc-4.2.1/libjava/java/lang/natPosixProcess.cc | |
| parent | 8d401cf711539af5a2f78d12447341d774892618 (diff) | |
| download | toolchain_gcc-df62c1c110e8532b995b23540b7e3695729c0779.tar.gz toolchain_gcc-df62c1c110e8532b995b23540b7e3695729c0779.tar.bz2 toolchain_gcc-df62c1c110e8532b995b23540b7e3695729c0779.zip | |
Check in gcc sources for prebuilt toolchains in Eclair.
Diffstat (limited to 'gcc-4.2.1/libjava/java/lang/natPosixProcess.cc')
| -rw-r--r-- | gcc-4.2.1/libjava/java/lang/natPosixProcess.cc | 455 |
1 files changed, 455 insertions, 0 deletions
diff --git a/gcc-4.2.1/libjava/java/lang/natPosixProcess.cc b/gcc-4.2.1/libjava/java/lang/natPosixProcess.cc new file mode 100644 index 000000000..77ac69253 --- /dev/null +++ b/gcc-4.2.1/libjava/java/lang/natPosixProcess.cc @@ -0,0 +1,455 @@ +// natPosixProcess.cc - Native side of POSIX process code. + +/* Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004, 2005, 2006 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. */ + +#include <config.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <errno.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/wait.h> +#ifdef HAVE_SYS_RESOURCE_H +#include <sys/resource.h> +#endif +#include <signal.h> +#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> +#include <java/io/File.h> +#include <java/io/FileDescriptor.h> +#include <gnu/java/nio/channels/FileChannelImpl.h> +#include <java/io/FileInputStream.h> +#include <java/io/FileOutputStream.h> +#include <java/io/IOException.h> +#include <java/lang/OutOfMemoryError.h> + +using gnu::java::nio::channels::FileChannelImpl; + +extern char **environ; + +static char * +new_string (jstring string) +{ + jsize s = _Jv_GetStringUTFLength (string); + char *buf = (char *) _Jv_Malloc (s + 1); + _Jv_GetStringUTFRegion (string, 0, string->length(), buf); + buf[s] = '\0'; + return buf; +} + +static void +cleanup (char **args, char **env, char *path) +{ + if (args != NULL) + { + for (int i = 0; args[i] != NULL; ++i) + _Jv_Free (args[i]); + _Jv_Free (args); + } + if (env != NULL) + { + for (int i = 0; env[i] != NULL; ++i) + _Jv_Free (env[i]); + _Jv_Free (env); + } + if (path != NULL) + _Jv_Free (path); +} + +// This makes our error handling a bit simpler and it lets us avoid +// thread bugs where we close a possibly-reopened file descriptor for +// a second time. +static void +myclose (int &fd) +{ + if (fd != -1) + close (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$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 () +{ + // Wait for SIGCHLD + sigset_t mask; + pthread_sigmask (0, NULL, &mask); + sigdelset (&mask, SIGCHLD); + + // Use sigsuspend() instead of sigwait() as sigwait() doesn't play + // nicely with the GC's use of signals. + sigsuspend (&mask); + + // Do not check sigsuspend return value. The only legitimate return + // is EINTR, but there is a known kernel bug affecting alpha-linux + // wrt sigsuspend+handler+sigreturn that can result in a return value + // of __NR_sigsuspend and errno unset. Don't fail unnecessarily on + // older kernel versions. + + // All OK. + return; +} + +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); + + // Note that if process==NULL, then we have an unknown child. + // This is not common, but can happen, and isn't an error. + if (process) + { + JvSynchronize sync (process); + process->status = WIFEXITED (status) ? WEXITSTATUS (status) : -1; + process->state = ConcreteProcess::STATE_TERMINATED; + process->processTerminationCleanup(); + process->notifyAll (); + } + } + +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; + + // Initialize all locals here to make cleanup simpler. + char **args = NULL; + char **env = NULL; + char *path = NULL; + int inp[2], outp[2], errp[2], msgp[2]; + inp[0] = -1; + inp[1] = -1; + outp[0] = -1; + outp[1] = -1; + errp[0] = -1; + errp[1] = -1; + msgp[0] = -1; + msgp[1] = -1; + errorStream = NULL; + inputStream = NULL; + outputStream = NULL; + + try + { + // Transform arrays to native form. + args = (char **) _Jv_Malloc ((progarray->length + 1) * sizeof (char *)); + + // Initialize so we can gracefully recover. + jstring *elts = elements (progarray); + for (int i = 0; i <= progarray->length; ++i) + args[i] = NULL; + + for (int i = 0; i < progarray->length; ++i) + args[i] = new_string (elts[i]); + args[progarray->length] = NULL; + + if (envp) + { + env = (char **) _Jv_Malloc ((envp->length + 1) * sizeof (char *)); + elts = elements (envp); + + // Initialize so we can gracefully recover. + for (int i = 0; i <= envp->length; ++i) + env[i] = NULL; + + for (int i = 0; i < envp->length; ++i) + env[i] = new_string (elts[i]); + env[envp->length] = NULL; + } + + // We allocate this here because we can't call malloc() after + // the fork. + if (dir != NULL) + path = new_string (dir->getPath ()); + + // Create pipes for I/O. MSGP is for communicating exec() + // status. + if (pipe (inp) || pipe (outp) || pipe (errp) || pipe (msgp) + || fcntl (msgp[1], F_SETFD, FD_CLOEXEC)) + 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)); + + // We don't use vfork() because that would cause the local + // environment to be set by the child. + + // 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 + // explicitly. + char *path_val = getenv ("PATH"); + char *ld_path_val = getenv ("LD_LIBRARY_PATH"); + environ = env; + if (path_val && getenv ("PATH") == NULL) + { + 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); + strcpy (ld_path_env, "LD_LIBRARY_PATH="); + strcat (ld_path_env, ld_path_val); + putenv (ld_path_env); + } + } + + // We ignore errors from dup2 because they should never occur. + dup2 (outp[0], 0); + dup2 (inp[1], 1); + dup2 (errp[1], 2); + + // Use close and not myclose -- we're in the child, and we + // aren't worried about the possible race condition. + close (inp[0]); + close (inp[1]); + close (errp[0]); + close (errp[1]); + close (outp[0]); + close (outp[1]); + close (msgp[0]); + + // Change directory. + if (path != NULL) + { + if (chdir (path) != 0) + { + char c = errno; + write (msgp[1], &c, 1); + _exit (127); + } + } + // Make sure all file descriptors are closed. In + // multi-threaded programs, there is a race between when a + // descriptor is obtained, when we can set FD_CLOEXEC, and + // fork(). If the fork occurs before FD_CLOEXEC is set, the + // descriptor would leak to the execed process if we did not + // manually close it. So that is what we do. Since we + // close all the descriptors, it is redundant to set + // FD_CLOEXEC on them elsewhere. + int max_fd; +#ifdef HAVE_GETRLIMIT + rlimit rl; + int rv = getrlimit(RLIMIT_NOFILE, &rl); + if (rv == 0) + max_fd = rl.rlim_max - 1; + else + max_fd = 1024 - 1; +#else + max_fd = 1024 - 1; +#endif + while(max_fd > 2) + { + if (max_fd != msgp[1]) + close (max_fd); + max_fd--; + } + // Make sure that SIGCHLD is unblocked for the new process. + sigset_t mask; + sigemptyset (&mask); + sigaddset (&mask, SIGCHLD); + sigprocmask (SIG_UNBLOCK, &mask, NULL); + + execvp (args[0], args); + + // Send the parent notification that the exec failed. + char c = errno; + write (msgp[1], &c, 1); + _exit (127); + } + + // 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]); + myclose (msgp[1]); + + char c; + int r = read (msgp[0], &c, 1); + if (r == -1) + throw new IOException (JvNewStringUTF (strerror (errno))); + else if (r != 0) + throw new IOException (JvNewStringUTF (strerror (c))); + } + catch (java::lang::Throwable *thrown) + { + // Do some cleanup we only do on failure. If a stream object + // has been created, we must close the stream itself (to avoid + // duplicate closes when the stream object is collected). + // Otherwise we simply close the underlying file descriptor. + // We ignore errors here as they are uninteresting. + + try + { + if (inputStream != NULL) + inputStream->close (); + else + myclose (inp[0]); + } + catch (java::lang::Throwable *ignore) + { + } + + try + { + if (outputStream != NULL) + outputStream->close (); + else + myclose (outp[1]); + } + catch (java::lang::Throwable *ignore) + { + } + + try + { + if (errorStream != NULL) + errorStream->close (); + else + myclose (errp[0]); + } + catch (java::lang::Throwable *ignore) + { + } + + // These are potentially duplicate, but it doesn't matter due to + // the use of myclose. + myclose (outp[0]); + myclose (inp[1]); + myclose (errp[1]); + myclose (msgp[1]); + + exception = thrown; + } + + myclose (msgp[0]); + cleanup (args, env, path); +} |
