diff options
Diffstat (limited to 'libjava/java/lang/natPosixProcess.cc')
-rw-r--r-- | libjava/java/lang/natPosixProcess.cc | 157 |
1 files changed, 85 insertions, 72 deletions
diff --git a/libjava/java/lang/natPosixProcess.cc b/libjava/java/lang/natPosixProcess.cc index 252da6e80ab..c7b8b6e5eb0 100644 --- a/libjava/java/lang/natPosixProcess.cc +++ b/libjava/java/lang/natPosixProcess.cc @@ -30,6 +30,8 @@ details. */ #include <gcj/cni.h> #include <jvm.h> +#include <posix.h> +#include <posix-threads.h> #include <java/lang/PosixProcess$ProcessManager.h> #include <java/lang/PosixProcess.h> @@ -48,6 +50,7 @@ details. */ #include <java/lang/PosixProcess$EOFInputStream.h> using gnu::java::nio::channels::FileChannelImpl; +using namespace java::lang; extern char **environ; @@ -91,13 +94,37 @@ myclose (int &fd) fd = -1; } +namespace +{ + struct ProcessManagerInternal + { + int pipe_ends[2]; + struct sigaction old_sigaction; + }; +} + + // 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) +sigchld_handler (int sig, siginfo_t *si, void *third) { - // Ignore. + if (PosixProcess$ProcessManager::nativeData != NULL) + { + ProcessManagerInternal *pmi = + (ProcessManagerInternal *)PosixProcess$ProcessManager::nativeData; + char c = 0; + ::write(pmi->pipe_ends[1], &c, 1); + if (pmi->old_sigaction.sa_handler != SIG_DFL + && pmi->old_sigaction.sa_handler != SIG_IGN) + { + if ((pmi->old_sigaction.sa_flags & SA_SIGINFO) != 0) + pmi->old_sigaction.sa_sigaction(sig, si, third); + else + (*pmi->old_sigaction.sa_handler)(sig); + } + } } @@ -105,22 +132,35 @@ sigchld_handler (int) void java::lang::PosixProcess$ProcessManager::init () { - using namespace java::lang; - // Remenber our PID so other threads can kill us. - reaperPID = (jlong) pthread_self (); + // The nativeData is static to avoid races installing the signal + // handler in the case that it is chained. + if (nativeData == NULL ) + { + ProcessManagerInternal *pmi = + (ProcessManagerInternal *)JvAllocBytes(sizeof(ProcessManagerInternal)); - // SIGCHLD is blocked in all threads in posix-threads.cc. - // Setup the SIGCHLD handler. - struct sigaction sa; - memset (&sa, 0, sizeof (sa)); + if (0 != ::pipe(pmi->pipe_ends)) + goto error; - sa.sa_handler = sigchld_handler; - // We only want signals when the things exit. - sa.sa_flags = SA_NOCLDSTOP; + // Make writing non-blocking so that the signal handler will + // never block. + int fl = ::fcntl(pmi->pipe_ends[1], F_GETFL); + ::fcntl(pmi->pipe_ends[1], F_SETFL, fl | O_NONBLOCK); - if (-1 == sigaction (SIGCHLD, &sa, NULL)) - goto error; + nativeData = (::gnu::gcj::RawDataManaged *)pmi; + // SIGCHLD is blocked in all threads in posix-threads.cc. + // Setup the SIGCHLD handler. + struct sigaction sa; + memset (&sa, 0, sizeof (sa)); + + sa.sa_sigaction = sigchld_handler; + // We only want signals when the things exit. + sa.sa_flags = SA_NOCLDSTOP | SA_SIGINFO; + + if (-1 == sigaction (SIGCHLD, &sa, &pmi->old_sigaction)) + goto error; + } // All OK. return; @@ -132,79 +172,52 @@ void java::lang::PosixProcess$ProcessManager::waitForSignal () { // Wait for SIGCHLD - sigset_t mask; - pthread_sigmask (0, NULL, &mask); - sigdelset (&mask, SIGCHLD); + _Jv_UnBlockSigchld(); + ProcessManagerInternal *pmi = (ProcessManagerInternal *)nativeData; - // Use sigsuspend() instead of sigwait() as sigwait() doesn't play - // nicely with the GC's use of signals. - sigsuspend (&mask); + // Try to read multiple (64) notifications in one go. + char c[64]; + ::read(pmi->pipe_ends[0], c, sizeof (c)); - // 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. + _Jv_BlockSigchld(); - // All OK. return; } -jboolean java::lang::PosixProcess$ProcessManager::reap () +jboolean java::lang::PosixProcess$ProcessManager::reap (PosixProcess *p) { - 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. - PosixProcess * 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 = PosixProcess::STATE_TERMINATED; - process->processTerminationCleanup(); - process->notifyAll (); - } - } - -error: - throw new InternalError (JvNewStringUTF (strerror (errno))); + pid_t rv; + + // Try to get the return code from the child process. + int status; + rv = ::waitpid ((pid_t)p->pid, &status, WNOHANG); + if (rv == -1) + throw new InternalError (JvNewStringUTF (strerror (errno))); + + if (rv == 0) + return false; // No children to wait for. + + JvSynchronize sync (p); + p->status = WIFEXITED (status) ? WEXITSTATUS (status) : -1; + p->state = PosixProcess::STATE_TERMINATED; + p->processTerminationCleanup(); + p->notifyAll (); + return true; } void java::lang::PosixProcess$ProcessManager::signalReaper () { - int c = pthread_kill ((pthread_t) reaperPID, SIGCHLD); - if (c == 0) - return; - // pthread_kill() failed. - throw new InternalError (JvNewStringUTF (strerror (c))); + ProcessManagerInternal *pmi = (ProcessManagerInternal *)nativeData; + char c = 0; + ::write(pmi->pipe_ends[1], &c, 1); + // Ignore errors. If EPIPE the reaper has already exited. } void java::lang::PosixProcess::nativeDestroy () { - int c = kill ((pid_t) pid, SIGKILL); + int c = ::kill ((pid_t) pid, SIGKILL); if (c == 0) return; // kill() failed. @@ -427,9 +440,9 @@ java::lang::PosixProcess::nativeSpawn () char c; int r = read (msgp[0], &c, 1); if (r == -1) - throw new IOException (JvNewStringUTF (strerror (errno))); + throw new IOException (JvNewStringUTF (strerror (errno))); else if (r != 0) - throw new IOException (JvNewStringUTF (strerror (c))); + throw new IOException (JvNewStringUTF (strerror (c))); } catch (java::lang::Throwable *thrown) { |