| Index: runtime/bin/process_linux.cc
|
| diff --git a/runtime/bin/process_linux.cc b/runtime/bin/process_linux.cc
|
| index adf5cd4177db26ee0d277a4e08fd895aaafe9b8e..bb4ad872f060209f7c6b5c6ad4734d0d459e7d55 100644
|
| --- a/runtime/bin/process_linux.cc
|
| +++ b/runtime/bin/process_linux.cc
|
| @@ -10,7 +10,6 @@
|
| #include <errno.h> // NOLINT
|
| #include <fcntl.h> // NOLINT
|
| #include <poll.h> // NOLINT
|
| -#include <signal.h> // NOLINT
|
| #include <stdio.h> // NOLINT
|
| #include <stdlib.h> // NOLINT
|
| #include <string.h> // NOLINT
|
| @@ -111,161 +110,121 @@ ProcessInfo* ProcessInfoList::active_processes_ = NULL;
|
| dart::Mutex* ProcessInfoList::mutex_ = new dart::Mutex();
|
|
|
|
|
| -// The exit code handler sets up a separate thread which is signalled
|
| -// on SIGCHLD. That separate thread can then get the exit code from
|
| +// The exit code handler sets up a separate thread which waits for child
|
| +// processes to terminate. That separate thread can then get the exit code from
|
| // processes that have exited and communicate it to Dart through the
|
| // event loop.
|
| class ExitCodeHandler {
|
| public:
|
| - // Ensure that the ExitCodeHandler has been initialized.
|
| - static bool EnsureInitialized() {
|
| + // Notify the ExitCodeHandler that another process exists.
|
| + static void ProcessStarted() {
|
| // Multiple isolates could be starting processes at the same
|
| - // time. Make sure that only one of them initializes the
|
| - // ExitCodeHandler.
|
| - MutexLocker locker(mutex_);
|
| - if (initialized_) {
|
| - return true;
|
| - }
|
| + // time. Make sure that only one ExitCodeHandler thread exists.
|
| + MonitorLocker locker(monitor_);
|
| + process_count_++;
|
| +
|
| + monitor_->Notify();
|
|
|
| - // Allocate a pipe that the signal handler can write a byte to and
|
| - // that the exit handler thread can poll.
|
| - int result = TEMP_FAILURE_RETRY(pipe(sig_chld_fds_));
|
| - if (result < 0) {
|
| - return false;
|
| + if (running_) {
|
| + return;
|
| }
|
| - FDUtils::SetCloseOnExec(sig_chld_fds_[0]);
|
| - FDUtils::SetCloseOnExec(sig_chld_fds_[1]);
|
|
|
| - // Start thread that polls the pipe and handles process exits when
|
| - // data is received on the pipe.
|
| - result = dart::Thread::Start(ExitCodeHandlerEntry, sig_chld_fds_[0]);
|
| + // Start thread that handles process exits when wait returns.
|
| + int result = dart::Thread::Start(ExitCodeHandlerEntry, 0);
|
| if (result != 0) {
|
| FATAL1("Failed to start exit code handler worker thread %d", result);
|
| }
|
|
|
| - // Mark write end non-blocking.
|
| - FDUtils::SetNonBlocking(sig_chld_fds_[1]);
|
| -
|
| - // Thread started and the ExitCodeHandler is initialized.
|
| - initialized_ = true;
|
| - return true;
|
| - }
|
| -
|
| - // Get the write end of the pipe.
|
| - static int WakeUpFd() {
|
| - return sig_chld_fds_[1];
|
| + running_ = true;
|
| }
|
|
|
| static void TerminateExitCodeThread() {
|
| - MutexLocker locker(mutex_);
|
| - if (!initialized_) {
|
| + MonitorLocker locker(monitor_);
|
| +
|
| + if (!running_) {
|
| return;
|
| }
|
|
|
| - uint8_t data = kThreadTerminateByte;
|
| - ssize_t result =
|
| - TEMP_FAILURE_RETRY(write(ExitCodeHandler::WakeUpFd(), &data, 1));
|
| - if (result < 1) {
|
| - perror("Failed to write to wake-up fd to terminate exit code thread");
|
| - }
|
| + // Set terminate_done_ to false, so we can use it as a guard for our
|
| + // monitor.
|
| + running_ = false;
|
|
|
| - {
|
| - MonitorLocker terminate_locker(thread_terminate_monitor_);
|
| - while (!thread_terminated_) {
|
| - terminate_locker.Wait();
|
| - }
|
| + // Fork to wake up waitpid.
|
| + if (TEMP_FAILURE_RETRY(fork()) == 0) {
|
| + exit(0);
|
| }
|
| - }
|
| -
|
| - static void ExitCodeThreadTerminated() {
|
| - MonitorLocker locker(thread_terminate_monitor_);
|
| - thread_terminated_ = true;
|
| - locker.Notify();
|
| - }
|
|
|
| - private:
|
| - static const uint8_t kThreadTerminateByte = 1;
|
| + monitor_->Notify();
|
|
|
| - // GetProcessExitCodes is called on a separate thread when a SIGCHLD
|
| - // signal is received to retrieve the exit codes and post them to
|
| - // dart.
|
| - static void GetProcessExitCodes() {
|
| - pid_t pid = 0;
|
| - int status = 0;
|
| - while ((pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG))) > 0) {
|
| - int exit_code = 0;
|
| - int negative = 0;
|
| - if (WIFEXITED(status)) {
|
| - exit_code = WEXITSTATUS(status);
|
| - }
|
| - if (WIFSIGNALED(status)) {
|
| - exit_code = WTERMSIG(status);
|
| - negative = 1;
|
| - }
|
| - intptr_t exit_code_fd = ProcessInfoList::LookupProcessExitFd(pid);
|
| - if (exit_code_fd != 0) {
|
| - int message[2] = { exit_code, negative };
|
| - ssize_t result =
|
| - FDUtils::WriteToBlocking(exit_code_fd, &message, sizeof(message));
|
| - // If the process has been closed, the read end of the exit
|
| - // pipe has been closed. It is therefore not a problem that
|
| - // write fails with a broken pipe error. Other errors should
|
| - // not happen.
|
| - if (result != -1 && result != sizeof(message)) {
|
| - FATAL("Failed to write entire process exit message");
|
| - } else if (result == -1 && errno != EPIPE) {
|
| - FATAL1("Failed to write exit code: %d", errno);
|
| - }
|
| - ProcessInfoList::RemoveProcess(pid);
|
| - }
|
| + while (!terminate_done_) {
|
| + monitor_->Wait(dart::Monitor::kNoTimeout);
|
| }
|
| }
|
|
|
| -
|
| + private:
|
| // Entry point for the separate exit code handler thread started by
|
| // the ExitCodeHandler.
|
| static void ExitCodeHandlerEntry(uword param) {
|
| - struct pollfd pollfds;
|
| - pollfds.fd = param;
|
| - pollfds.events = POLLIN;
|
| + pid_t pid = 0;
|
| + int status = 0;
|
| while (true) {
|
| - int result = TEMP_FAILURE_RETRY(poll(&pollfds, 1, -1));
|
| - if (result == -1) {
|
| - ASSERT(EAGAIN == EWOULDBLOCK);
|
| - if (errno != EWOULDBLOCK) {
|
| - perror("ExitCodeHandler poll failed");
|
| + {
|
| + MonitorLocker locker(monitor_);
|
| + while (running_ && process_count_ == 0) {
|
| + monitor_->Wait(dart::Monitor::kNoTimeout);
|
| }
|
| - } else {
|
| - // Read the byte from the wake-up fd.
|
| - ASSERT(result = 1);
|
| - intptr_t data = 0;
|
| - ssize_t read_bytes = FDUtils::ReadFromBlocking(pollfds.fd, &data, 1);
|
| - if (read_bytes < 1) {
|
| - perror("Failed to read from wake-up fd in exit-code handler");
|
| - }
|
| - if (data == ExitCodeHandler::kThreadTerminateByte) {
|
| - ExitCodeThreadTerminated();
|
| + if (!running_) {
|
| + terminate_done_ = true;
|
| + monitor_->Notify();
|
| return;
|
| }
|
| - // Get the exit code from all processes that have died.
|
| - GetProcessExitCodes();
|
| + }
|
| +
|
| + if ((pid = TEMP_FAILURE_RETRY(wait(&status))) > 0) {
|
| + int exit_code = 0;
|
| + int negative = 0;
|
| + if (WIFEXITED(status)) {
|
| + exit_code = WEXITSTATUS(status);
|
| + }
|
| + if (WIFSIGNALED(status)) {
|
| + exit_code = WTERMSIG(status);
|
| + negative = 1;
|
| + }
|
| + intptr_t exit_code_fd = ProcessInfoList::LookupProcessExitFd(pid);
|
| + if (exit_code_fd != 0) {
|
| + int message[2] = { exit_code, negative };
|
| + ssize_t result =
|
| + FDUtils::WriteToBlocking(exit_code_fd, &message, sizeof(message));
|
| + // If the process has been closed, the read end of the exit
|
| + // pipe has been closed. It is therefore not a problem that
|
| + // write fails with a broken pipe error. Other errors should
|
| + // not happen.
|
| + if (result != -1 && result != sizeof(message)) {
|
| + FATAL("Failed to write entire process exit message");
|
| + } else if (result == -1 && errno != EPIPE) {
|
| + FATAL1("Failed to write exit code: %d", errno);
|
| + }
|
| + ProcessInfoList::RemoveProcess(pid);
|
| + {
|
| + MonitorLocker locker(monitor_);
|
| + process_count_--;
|
| + }
|
| + }
|
| }
|
| }
|
| }
|
|
|
| - static dart::Mutex* mutex_;
|
| - static bool initialized_;
|
| - static int sig_chld_fds_[2];
|
| - static bool thread_terminated_;
|
| - static dart::Monitor* thread_terminate_monitor_;
|
| + static bool terminate_done_;
|
| + static int process_count_;
|
| + static bool running_;
|
| + static dart::Monitor* monitor_;
|
| };
|
|
|
|
|
| -dart::Mutex* ExitCodeHandler::mutex_ = new dart::Mutex();
|
| -bool ExitCodeHandler::initialized_ = false;
|
| -int ExitCodeHandler::sig_chld_fds_[2] = { 0, 0 };
|
| -bool ExitCodeHandler::thread_terminated_ = false;
|
| -dart::Monitor* ExitCodeHandler::thread_terminate_monitor_ = new dart::Monitor();
|
| +bool ExitCodeHandler::running_ = false;
|
| +int ExitCodeHandler::process_count_ = 0;
|
| +bool ExitCodeHandler::terminate_done_ = false;
|
| +dart::Monitor* ExitCodeHandler::monitor_ = new dart::Monitor();
|
|
|
|
|
| static void SetChildOsErrorMessage(char** os_error_message) {
|
| @@ -275,21 +234,6 @@ static void SetChildOsErrorMessage(char** os_error_message) {
|
| }
|
|
|
|
|
| -static void SigChldHandler(int process_signal, siginfo_t* siginfo, void* tmp) {
|
| - // Save errno so it can be restored at the end.
|
| - int entry_errno = errno;
|
| - // Signal the exit code handler where the actual processing takes
|
| - // place.
|
| - ssize_t result =
|
| - TEMP_FAILURE_RETRY(write(ExitCodeHandler::WakeUpFd(), "", 1));
|
| - if (result < 1) {
|
| - perror("Failed to write to wake-up fd in SIGCHLD handler");
|
| - }
|
| - // Restore errno.
|
| - errno = entry_errno;
|
| -}
|
| -
|
| -
|
| static void ReportChildError(int exec_control_fd) {
|
| // In the case of failure in the child process write the errno and
|
| // the OS error message to the exec control pipe and exit.
|
| @@ -329,14 +273,6 @@ int Process::Start(const char* path,
|
| int exec_control[2]; // Pipe to get the result from exec.
|
| int result;
|
|
|
| - bool initialized = ExitCodeHandler::EnsureInitialized();
|
| - if (!initialized) {
|
| - SetChildOsErrorMessage(os_error_message);
|
| - Log::PrintErr("Error initializing exit code handler: %s\n",
|
| - *os_error_message);
|
| - return errno;
|
| - }
|
| -
|
| result = TEMP_FAILURE_RETRY(pipe(read_in));
|
| if (result < 0) {
|
| SetChildOsErrorMessage(os_error_message);
|
| @@ -412,13 +348,6 @@ int Process::Start(const char* path,
|
| program_environment[environment_length] = NULL;
|
| }
|
|
|
| - struct sigaction act;
|
| - bzero(&act, sizeof(act));
|
| - act.sa_sigaction = SigChldHandler;
|
| - act.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
|
| - if (sigaction(SIGCHLD, &act, 0) != 0) {
|
| - perror("Process start: setting signal handler failed");
|
| - }
|
| pid = TEMP_FAILURE_RETRY(fork());
|
| if (pid < 0) {
|
| SetChildOsErrorMessage(os_error_message);
|
| @@ -562,6 +491,9 @@ int Process::Start(const char* path,
|
| *err = read_err[0];
|
| TEMP_FAILURE_RETRY(close(read_err[1]));
|
|
|
| + // Be sure to listen for exit-codes, now we have a child-process.
|
| + ExitCodeHandler::ProcessStarted();
|
| +
|
| *id = pid;
|
| return 0;
|
| }
|
|
|