summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
authorrsesek@chromium.org <rsesek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-10 18:36:09 +0000
committerrsesek@chromium.org <rsesek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-10 18:36:09 +0000
commit307af2131ca46613b150c07b4b1b4a529faf2ece (patch)
treef7527104a9e682d9d5ad5a27821ceb6d470538c4 /base
parent79e30dda9361e31cd08c4ad42e31747cdd228f82 (diff)
downloadchromium_src-307af2131ca46613b150c07b4b1b4a529faf2ece.zip
chromium_src-307af2131ca46613b150c07b4b1b4a529faf2ece.tar.gz
chromium_src-307af2131ca46613b150c07b4b1b4a529faf2ece.tar.bz2
Split out process killing functions from base/process_util.h into base/process/kill.h.
BUG=242290 R=brettw@chromium.org Review URL: https://codereview.chromium.org/18555002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@210893 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r--base/base.gypi7
-rw-r--r--base/process/kill.cc (renamed from base/process_util.cc)9
-rw-r--r--base/process/kill.h144
-rw-r--r--base/process/kill_mac.cc173
-rw-r--r--base/process/kill_posix.cc493
-rw-r--r--base/process/kill_win.cc256
-rw-r--r--base/process_util.h122
-rw-r--r--base/process_util_mac.mm155
-rw-r--r--base/process_util_posix.cc464
-rw-r--r--base/process_util_win.cc223
10 files changed, 1079 insertions, 967 deletions
diff --git a/base/base.gypi b/base/base.gypi
index fc7cf04..0935b14 100644
--- a/base/base.gypi
+++ b/base/base.gypi
@@ -385,7 +385,6 @@
'process_info_win.cc',
'process_linux.cc',
'process_posix.cc',
- 'process_util.cc',
'process_util.h',
'process_util_freebsd.cc',
'process_util_ios.mm',
@@ -397,6 +396,11 @@
'process_win.cc',
'process/internal_linux.cc',
'process/internal_linux.h',
+ 'process/kill.cc',
+ 'process/kill.h',
+ 'process/kill_mac.cc',
+ 'process/kill_posix.cc',
+ 'process/kill_win.cc',
'process/memory.h',
'process/memory_linux.cc',
'process/memory_mac.mm',
@@ -694,6 +698,7 @@
'path_service.cc',
'platform_file_posix.cc',
'posix/unix_domain_socket_linux.cc',
+ 'process/kill_posix.cc',
'process_posix.cc',
'process_util.cc',
'process_util_posix.cc',
diff --git a/base/process_util.cc b/base/process/kill.cc
index 9b89c8f..caca348 100644
--- a/base/process_util.cc
+++ b/base/process/kill.cc
@@ -1,12 +1,15 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "base/process_util.h"
+#include "base/process/kill.h"
+
+#include "base/process/process_iterator.h"
namespace base {
-bool KillProcesses(const FilePath::StringType& executable_name, int exit_code,
+bool KillProcesses(const FilePath::StringType& executable_name,
+ int exit_code,
const ProcessFilter* filter) {
bool result = true;
NamedProcessIterator iter(executable_name, filter);
diff --git a/base/process/kill.h b/base/process/kill.h
new file mode 100644
index 0000000..f7ee14c
--- /dev/null
+++ b/base/process/kill.h
@@ -0,0 +1,144 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains routines to kill processes and get the exit code and
+// termination status.
+
+#ifndef BASE_PROCESS_KILL_H_
+#define BASE_PROCESS_KILL_H_
+
+#include "base/files/file_path.h"
+#include "base/process.h"
+#include "base/time/time.h"
+
+namespace base {
+
+class ProcessFilter;
+
+// Return status values from GetTerminationStatus. Don't use these as
+// exit code arguments to KillProcess*(), use platform/application
+// specific values instead.
+enum TerminationStatus {
+ TERMINATION_STATUS_NORMAL_TERMINATION, // zero exit status
+ TERMINATION_STATUS_ABNORMAL_TERMINATION, // non-zero exit status
+ TERMINATION_STATUS_PROCESS_WAS_KILLED, // e.g. SIGKILL or task manager kill
+ TERMINATION_STATUS_PROCESS_CRASHED, // e.g. Segmentation fault
+ TERMINATION_STATUS_STILL_RUNNING, // child hasn't exited yet
+ TERMINATION_STATUS_MAX_ENUM
+};
+
+// Attempts to kill all the processes on the current machine that were launched
+// from the given executable name, ending them with the given exit code. If
+// filter is non-null, then only processes selected by the filter are killed.
+// Returns true if all processes were able to be killed off, false if at least
+// one couldn't be killed.
+BASE_EXPORT bool KillProcesses(const FilePath::StringType& executable_name,
+ int exit_code,
+ const ProcessFilter* filter);
+
+// Attempts to kill the process identified by the given process
+// entry structure, giving it the specified exit code. If |wait| is true, wait
+// for the process to be actually terminated before returning.
+// Returns true if this is successful, false otherwise.
+BASE_EXPORT bool KillProcess(ProcessHandle process, int exit_code, bool wait);
+
+#if defined(OS_POSIX)
+// Attempts to kill the process group identified by |process_group_id|. Returns
+// true on success.
+BASE_EXPORT bool KillProcessGroup(ProcessHandle process_group_id);
+#endif // defined(OS_POSIX)
+
+#if defined(OS_WIN)
+BASE_EXPORT bool KillProcessById(ProcessId process_id,
+ int exit_code,
+ bool wait);
+#endif // defined(OS_WIN)
+
+// Get the termination status of the process by interpreting the
+// circumstances of the child process' death. |exit_code| is set to
+// the status returned by waitpid() on POSIX, and from
+// GetExitCodeProcess() on Windows. |exit_code| may be NULL if the
+// caller is not interested in it. Note that on Linux, this function
+// will only return a useful result the first time it is called after
+// the child exits (because it will reap the child and the information
+// will no longer be available).
+BASE_EXPORT TerminationStatus GetTerminationStatus(ProcessHandle handle,
+ int* exit_code);
+
+#if defined(OS_POSIX)
+// Wait for the process to exit and get the termination status. See
+// GetTerminationStatus for more information. On POSIX systems, we can't call
+// WaitForExitCode and then GetTerminationStatus as the child will be reaped
+// when WaitForExitCode return and this information will be lost.
+BASE_EXPORT TerminationStatus WaitForTerminationStatus(ProcessHandle handle,
+ int* exit_code);
+#endif // defined(OS_POSIX)
+
+// Waits for process to exit. On POSIX systems, if the process hasn't been
+// signaled then puts the exit code in |exit_code|; otherwise it's considered
+// a failure. On Windows |exit_code| is always filled. Returns true on success,
+// and closes |handle| in any case.
+BASE_EXPORT bool WaitForExitCode(ProcessHandle handle, int* exit_code);
+
+// Waits for process to exit. If it did exit within |timeout_milliseconds|,
+// then puts the exit code in |exit_code|, and returns true.
+// In POSIX systems, if the process has been signaled then |exit_code| is set
+// to -1. Returns false on failure (the caller is then responsible for closing
+// |handle|).
+// The caller is always responsible for closing the |handle|.
+BASE_EXPORT bool WaitForExitCodeWithTimeout(ProcessHandle handle,
+ int* exit_code,
+ base::TimeDelta timeout);
+
+// Wait for all the processes based on the named executable to exit. If filter
+// is non-null, then only processes selected by the filter are waited on.
+// Returns after all processes have exited or wait_milliseconds have expired.
+// Returns true if all the processes exited, false otherwise.
+BASE_EXPORT bool WaitForProcessesToExit(
+ const FilePath::StringType& executable_name,
+ base::TimeDelta wait,
+ const ProcessFilter* filter);
+
+// Wait for a single process to exit. Return true if it exited cleanly within
+// the given time limit. On Linux |handle| must be a child process, however
+// on Mac and Windows it can be any process.
+BASE_EXPORT bool WaitForSingleProcess(ProcessHandle handle,
+ base::TimeDelta wait);
+
+// Waits a certain amount of time (can be 0) for all the processes with a given
+// executable name to exit, then kills off any of them that are still around.
+// If filter is non-null, then only processes selected by the filter are waited
+// on. Killed processes are ended with the given exit code. Returns false if
+// any processes needed to be killed, true if they all exited cleanly within
+// the wait_milliseconds delay.
+BASE_EXPORT bool CleanupProcesses(const FilePath::StringType& executable_name,
+ base::TimeDelta wait,
+ int exit_code,
+ const ProcessFilter* filter);
+
+// This method ensures that the specified process eventually terminates, and
+// then it closes the given process handle.
+//
+// It assumes that the process has already been signalled to exit, and it
+// begins by waiting a small amount of time for it to exit. If the process
+// does not appear to have exited, then this function starts to become
+// aggressive about ensuring that the process terminates.
+//
+// On Linux this method does not block the calling thread.
+// On OS X this method may block for up to 2 seconds.
+//
+// NOTE: The process handle must have been opened with the PROCESS_TERMINATE
+// and SYNCHRONIZE permissions.
+//
+BASE_EXPORT void EnsureProcessTerminated(ProcessHandle process_handle);
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+// The nicer version of EnsureProcessTerminated() that is patient and will
+// wait for |process_handle| to finish and then reap it.
+BASE_EXPORT void EnsureProcessGetsReaped(ProcessHandle process_handle);
+#endif
+
+} // namespace base
+
+#endif // BASE_PROCESS_KILL_H_
diff --git a/base/process/kill_mac.cc b/base/process/kill_mac.cc
new file mode 100644
index 0000000..5ebca5d
--- /dev/null
+++ b/base/process/kill_mac.cc
@@ -0,0 +1,173 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/process/kill.h"
+
+#include <signal.h>
+#include <sys/event.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+
+namespace base {
+
+namespace {
+
+const int kWaitBeforeKillSeconds = 2;
+
+// Reap |child| process. This call blocks until completion.
+void BlockingReap(pid_t child) {
+ const pid_t result = HANDLE_EINTR(waitpid(child, NULL, 0));
+ if (result == -1) {
+ DPLOG(ERROR) << "waitpid(" << child << ", NULL, 0)";
+ }
+}
+
+// Waits for |timeout| seconds for the given |child| to exit and reap it. If
+// the child doesn't exit within the time specified, kills it.
+//
+// This function takes two approaches: first, it tries to use kqueue to
+// observe when the process exits. kevent can monitor a kqueue with a
+// timeout, so this method is preferred to wait for a specified period of
+// time. Once the kqueue indicates the process has exited, waitpid will reap
+// the exited child. If the kqueue doesn't provide an exit event notification,
+// before the timeout expires, or if the kqueue fails or misbehaves, the
+// process will be mercilessly killed and reaped.
+//
+// A child process passed to this function may be in one of several states:
+// running, terminated and not yet reaped, and (apparently, and unfortunately)
+// terminated and already reaped. Normally, a process will at least have been
+// asked to exit before this function is called, but this is not required.
+// If a process is terminating and unreaped, there may be a window between the
+// time that kqueue will no longer recognize it and when it becomes an actual
+// zombie that a non-blocking (WNOHANG) waitpid can reap. This condition is
+// detected when kqueue indicates that the process is not running and a
+// non-blocking waitpid fails to reap the process but indicates that it is
+// still running. In this event, a blocking attempt to reap the process
+// collects the known-dying child, preventing zombies from congregating.
+//
+// In the event that the kqueue misbehaves entirely, as it might under a
+// EMFILE condition ("too many open files", or out of file descriptors), this
+// function will forcibly kill and reap the child without delay. This
+// eliminates another potential zombie vector. (If you're out of file
+// descriptors, you're probably deep into something else, but that doesn't
+// mean that zombies be allowed to kick you while you're down.)
+//
+// The fact that this function seemingly can be called to wait on a child
+// that's not only already terminated but already reaped is a bit of a
+// problem: a reaped child's pid can be reclaimed and may refer to a distinct
+// process in that case. The fact that this function can seemingly be called
+// to wait on a process that's not even a child is also a problem: kqueue will
+// work in that case, but waitpid won't, and killing a non-child might not be
+// the best approach.
+void WaitForChildToDie(pid_t child, int timeout) {
+ DCHECK(child > 0);
+ DCHECK(timeout > 0);
+
+ // DON'T ADD ANY EARLY RETURNS TO THIS FUNCTION without ensuring that
+ // |child| has been reaped. Specifically, even if a kqueue, kevent, or other
+ // call fails, this function should fall back to the last resort of trying
+ // to kill and reap the process. Not observing this rule will resurrect
+ // zombies.
+
+ int result;
+
+ int kq = HANDLE_EINTR(kqueue());
+ if (kq == -1) {
+ DPLOG(ERROR) << "kqueue()";
+ } else {
+ file_util::ScopedFD auto_close_kq(&kq);
+
+ struct kevent change = {0};
+ EV_SET(&change, child, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
+ result = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL));
+
+ if (result == -1) {
+ if (errno != ESRCH) {
+ DPLOG(ERROR) << "kevent (setup " << child << ")";
+ } else {
+ // At this point, one of the following has occurred:
+ // 1. The process has died but has not yet been reaped.
+ // 2. The process has died and has already been reaped.
+ // 3. The process is in the process of dying. It's no longer
+ // kqueueable, but it may not be waitable yet either. Mark calls
+ // this case the "zombie death race".
+
+ result = HANDLE_EINTR(waitpid(child, NULL, WNOHANG));
+
+ if (result != 0) {
+ // A positive result indicates case 1. waitpid succeeded and reaped
+ // the child. A result of -1 indicates case 2. The child has already
+ // been reaped. In both of these cases, no further action is
+ // necessary.
+ return;
+ }
+
+ // |result| is 0, indicating case 3. The process will be waitable in
+ // short order. Fall back out of the kqueue code to kill it (for good
+ // measure) and reap it.
+ }
+ } else {
+ // Keep track of the elapsed time to be able to restart kevent if it's
+ // interrupted.
+ TimeDelta remaining_delta = TimeDelta::FromSeconds(timeout);
+ TimeTicks deadline = TimeTicks::Now() + remaining_delta;
+ result = -1;
+ struct kevent event = {0};
+ while (remaining_delta.InMilliseconds() > 0) {
+ const struct timespec remaining_timespec = remaining_delta.ToTimeSpec();
+ result = kevent(kq, NULL, 0, &event, 1, &remaining_timespec);
+ if (result == -1 && errno == EINTR) {
+ remaining_delta = deadline - TimeTicks::Now();
+ result = 0;
+ } else {
+ break;
+ }
+ }
+
+ if (result == -1) {
+ DPLOG(ERROR) << "kevent (wait " << child << ")";
+ } else if (result > 1) {
+ DLOG(ERROR) << "kevent (wait " << child << "): unexpected result "
+ << result;
+ } else if (result == 1) {
+ if ((event.fflags & NOTE_EXIT) &&
+ (event.ident == static_cast<uintptr_t>(child))) {
+ // The process is dead or dying. This won't block for long, if at
+ // all.
+ BlockingReap(child);
+ return;
+ } else {
+ DLOG(ERROR) << "kevent (wait " << child
+ << "): unexpected event: fflags=" << event.fflags
+ << ", ident=" << event.ident;
+ }
+ }
+ }
+ }
+
+ // The child is still alive, or is very freshly dead. Be sure by sending it
+ // a signal. This is safe even if it's freshly dead, because it will be a
+ // zombie (or on the way to zombiedom) and kill will return 0 even if the
+ // signal is not delivered to a live process.
+ result = kill(child, SIGKILL);
+ if (result == -1) {
+ DPLOG(ERROR) << "kill(" << child << ", SIGKILL)";
+ } else {
+ // The child is definitely on the way out now. BlockingReap won't need to
+ // wait for long, if at all.
+ BlockingReap(child);
+ }
+}
+
+} // namespace
+
+void EnsureProcessTerminated(ProcessHandle process) {
+ WaitForChildToDie(process, kWaitBeforeKillSeconds);
+}
+
+} // namespace base
diff --git a/base/process/kill_posix.cc b/base/process/kill_posix.cc
new file mode 100644
index 0000000..61a6020
--- /dev/null
+++ b/base/process/kill_posix.cc
@@ -0,0 +1,493 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/process/kill.h"
+
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/process/process_iterator.h"
+#include "base/process_util.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+#include "base/threading/platform_thread.h"
+
+namespace base {
+
+namespace {
+
+int WaitpidWithTimeout(ProcessHandle handle,
+ int64 wait_milliseconds,
+ bool* success) {
+ // This POSIX version of this function only guarantees that we wait no less
+ // than |wait_milliseconds| for the process to exit. The child process may
+ // exit sometime before the timeout has ended but we may still block for up
+ // to 256 milliseconds after the fact.
+ //
+ // waitpid() has no direct support on POSIX for specifying a timeout, you can
+ // either ask it to block indefinitely or return immediately (WNOHANG).
+ // When a child process terminates a SIGCHLD signal is sent to the parent.
+ // Catching this signal would involve installing a signal handler which may
+ // affect other parts of the application and would be difficult to debug.
+ //
+ // Our strategy is to call waitpid() once up front to check if the process
+ // has already exited, otherwise to loop for wait_milliseconds, sleeping for
+ // at most 256 milliseconds each time using usleep() and then calling
+ // waitpid(). The amount of time we sleep starts out at 1 milliseconds, and
+ // we double it every 4 sleep cycles.
+ //
+ // usleep() is speced to exit if a signal is received for which a handler
+ // has been installed. This means that when a SIGCHLD is sent, it will exit
+ // depending on behavior external to this function.
+ //
+ // This function is used primarily for unit tests, if we want to use it in
+ // the application itself it would probably be best to examine other routes.
+ int status = -1;
+ pid_t ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG));
+ static const int64 kMaxSleepInMicroseconds = 1 << 18; // ~256 milliseconds.
+ int64 max_sleep_time_usecs = 1 << 10; // ~1 milliseconds.
+ int64 double_sleep_time = 0;
+
+ // If the process hasn't exited yet, then sleep and try again.
+ TimeTicks wakeup_time = TimeTicks::Now() +
+ TimeDelta::FromMilliseconds(wait_milliseconds);
+ while (ret_pid == 0) {
+ TimeTicks now = TimeTicks::Now();
+ if (now > wakeup_time)
+ break;
+ // Guaranteed to be non-negative!
+ int64 sleep_time_usecs = (wakeup_time - now).InMicroseconds();
+ // Sleep for a bit while we wait for the process to finish.
+ if (sleep_time_usecs > max_sleep_time_usecs)
+ sleep_time_usecs = max_sleep_time_usecs;
+
+ // usleep() will return 0 and set errno to EINTR on receipt of a signal
+ // such as SIGCHLD.
+ usleep(sleep_time_usecs);
+ ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG));
+
+ if ((max_sleep_time_usecs < kMaxSleepInMicroseconds) &&
+ (double_sleep_time++ % 4 == 0)) {
+ max_sleep_time_usecs *= 2;
+ }
+ }
+
+ if (success)
+ *success = (ret_pid != -1);
+
+ return status;
+}
+
+TerminationStatus GetTerminationStatusImpl(ProcessHandle handle,
+ bool can_block,
+ int* exit_code) {
+ int status = 0;
+ const pid_t result = HANDLE_EINTR(waitpid(handle, &status,
+ can_block ? 0 : WNOHANG));
+ if (result == -1) {
+ DPLOG(ERROR) << "waitpid(" << handle << ")";
+ if (exit_code)
+ *exit_code = 0;
+ return TERMINATION_STATUS_NORMAL_TERMINATION;
+ } else if (result == 0) {
+ // the child hasn't exited yet.
+ if (exit_code)
+ *exit_code = 0;
+ return TERMINATION_STATUS_STILL_RUNNING;
+ }
+
+ if (exit_code)
+ *exit_code = status;
+
+ if (WIFSIGNALED(status)) {
+ switch (WTERMSIG(status)) {
+ case SIGABRT:
+ case SIGBUS:
+ case SIGFPE:
+ case SIGILL:
+ case SIGSEGV:
+ return TERMINATION_STATUS_PROCESS_CRASHED;
+ case SIGINT:
+ case SIGKILL:
+ case SIGTERM:
+ return TERMINATION_STATUS_PROCESS_WAS_KILLED;
+ default:
+ break;
+ }
+ }
+
+ if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
+ return TERMINATION_STATUS_ABNORMAL_TERMINATION;
+
+ return TERMINATION_STATUS_NORMAL_TERMINATION;
+}
+
+} // namespace
+
+// Attempts to kill the process identified by the given process
+// entry structure. Ignores specified exit_code; posix can't force that.
+// Returns true if this is successful, false otherwise.
+bool KillProcess(ProcessHandle process_id, int exit_code, bool wait) {
+ DCHECK_GT(process_id, 1) << " tried to kill invalid process_id";
+ if (process_id <= 1)
+ return false;
+ bool result = kill(process_id, SIGTERM) == 0;
+ if (result && wait) {
+ int tries = 60;
+
+ if (RunningOnValgrind()) {
+ // Wait for some extra time when running under Valgrind since the child
+ // processes may take some time doing leak checking.
+ tries *= 2;
+ }
+
+ unsigned sleep_ms = 4;
+
+ // The process may not end immediately due to pending I/O
+ bool exited = false;
+ while (tries-- > 0) {
+ pid_t pid = HANDLE_EINTR(waitpid(process_id, NULL, WNOHANG));
+ if (pid == process_id) {
+ exited = true;
+ break;
+ }
+ if (pid == -1) {
+ if (errno == ECHILD) {
+ // The wait may fail with ECHILD if another process also waited for
+ // the same pid, causing the process state to get cleaned up.
+ exited = true;
+ break;
+ }
+ DPLOG(ERROR) << "Error waiting for process " << process_id;
+ }
+
+ usleep(sleep_ms * 1000);
+ const unsigned kMaxSleepMs = 1000;
+ if (sleep_ms < kMaxSleepMs)
+ sleep_ms *= 2;
+ }
+
+ // If we're waiting and the child hasn't died by now, force it
+ // with a SIGKILL.
+ if (!exited)
+ result = kill(process_id, SIGKILL) == 0;
+ }
+
+ if (!result)
+ DPLOG(ERROR) << "Unable to terminate process " << process_id;
+
+ return result;
+}
+
+bool KillProcessGroup(ProcessHandle process_group_id) {
+ bool result = kill(-1 * process_group_id, SIGKILL) == 0;
+ if (!result)
+ DPLOG(ERROR) << "Unable to terminate process group " << process_group_id;
+ return result;
+}
+
+TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) {
+ return GetTerminationStatusImpl(handle, false /* can_block */, exit_code);
+}
+
+TerminationStatus WaitForTerminationStatus(ProcessHandle handle,
+ int* exit_code) {
+ return GetTerminationStatusImpl(handle, true /* can_block */, exit_code);
+}
+
+bool WaitForExitCode(ProcessHandle handle, int* exit_code) {
+ int status;
+ if (HANDLE_EINTR(waitpid(handle, &status, 0)) == -1) {
+ NOTREACHED();
+ return false;
+ }
+
+ if (WIFEXITED(status)) {
+ *exit_code = WEXITSTATUS(status);
+ return true;
+ }
+
+ // If it didn't exit cleanly, it must have been signaled.
+ DCHECK(WIFSIGNALED(status));
+ return false;
+}
+
+bool WaitForExitCodeWithTimeout(ProcessHandle handle,
+ int* exit_code,
+ base::TimeDelta timeout) {
+ bool waitpid_success = false;
+ int status = WaitpidWithTimeout(handle, timeout.InMilliseconds(),
+ &waitpid_success);
+ if (status == -1)
+ return false;
+ if (!waitpid_success)
+ return false;
+ if (WIFSIGNALED(status)) {
+ *exit_code = -1;
+ return true;
+ }
+ if (WIFEXITED(status)) {
+ *exit_code = WEXITSTATUS(status);
+ return true;
+ }
+ return false;
+}
+
+bool WaitForProcessesToExit(const FilePath::StringType& executable_name,
+ base::TimeDelta wait,
+ const ProcessFilter* filter) {
+ bool result = false;
+
+ // TODO(port): This is inefficient, but works if there are multiple procs.
+ // TODO(port): use waitpid to avoid leaving zombies around
+
+ base::TimeTicks end_time = base::TimeTicks::Now() + wait;
+ do {
+ NamedProcessIterator iter(executable_name, filter);
+ if (!iter.NextProcessEntry()) {
+ result = true;
+ break;
+ }
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
+ } while ((end_time - base::TimeTicks::Now()) > base::TimeDelta());
+
+ return result;
+}
+
+#if defined(OS_MACOSX)
+// Using kqueue on Mac so that we can wait on non-child processes.
+// We can't use kqueues on child processes because we need to reap
+// our own children using wait.
+static bool WaitForSingleNonChildProcess(ProcessHandle handle,
+ base::TimeDelta wait) {
+ DCHECK_GT(handle, 0);
+ DCHECK(wait.InMilliseconds() == base::kNoTimeout || wait > base::TimeDelta());
+
+ int kq = kqueue();
+ if (kq == -1) {
+ DPLOG(ERROR) << "kqueue";
+ return false;
+ }
+ file_util::ScopedFD kq_closer(&kq);
+
+ struct kevent change = {0};
+ EV_SET(&change, handle, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
+ int result = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL));
+ if (result == -1) {
+ if (errno == ESRCH) {
+ // If the process wasn't found, it must be dead.
+ return true;
+ }
+
+ DPLOG(ERROR) << "kevent (setup " << handle << ")";
+ return false;
+ }
+
+ // Keep track of the elapsed time to be able to restart kevent if it's
+ // interrupted.
+ bool wait_forever = wait.InMilliseconds() == base::kNoTimeout;
+ base::TimeDelta remaining_delta;
+ base::TimeTicks deadline;
+ if (!wait_forever) {
+ remaining_delta = wait;
+ deadline = base::TimeTicks::Now() + remaining_delta;
+ }
+
+ result = -1;
+ struct kevent event = {0};
+
+ while (wait_forever || remaining_delta > base::TimeDelta()) {
+ struct timespec remaining_timespec;
+ struct timespec* remaining_timespec_ptr;
+ if (wait_forever) {
+ remaining_timespec_ptr = NULL;
+ } else {
+ remaining_timespec = remaining_delta.ToTimeSpec();
+ remaining_timespec_ptr = &remaining_timespec;
+ }
+
+ result = kevent(kq, NULL, 0, &event, 1, remaining_timespec_ptr);
+
+ if (result == -1 && errno == EINTR) {
+ if (!wait_forever) {
+ remaining_delta = deadline - base::TimeTicks::Now();
+ }
+ result = 0;
+ } else {
+ break;
+ }
+ }
+
+ if (result < 0) {
+ DPLOG(ERROR) << "kevent (wait " << handle << ")";
+ return false;
+ } else if (result > 1) {
+ DLOG(ERROR) << "kevent (wait " << handle << "): unexpected result "
+ << result;
+ return false;
+ } else if (result == 0) {
+ // Timed out.
+ return false;
+ }
+
+ DCHECK_EQ(result, 1);
+
+ if (event.filter != EVFILT_PROC ||
+ (event.fflags & NOTE_EXIT) == 0 ||
+ event.ident != static_cast<uintptr_t>(handle)) {
+ DLOG(ERROR) << "kevent (wait " << handle
+ << "): unexpected event: filter=" << event.filter
+ << ", fflags=" << event.fflags
+ << ", ident=" << event.ident;
+ return false;
+ }
+
+ return true;
+}
+#endif // OS_MACOSX
+
+bool WaitForSingleProcess(ProcessHandle handle, base::TimeDelta wait) {
+ ProcessHandle parent_pid = GetParentProcessId(handle);
+ ProcessHandle our_pid = Process::Current().handle();
+ if (parent_pid != our_pid) {
+#if defined(OS_MACOSX)
+ // On Mac we can wait on non child processes.
+ return WaitForSingleNonChildProcess(handle, wait);
+#else
+ // Currently on Linux we can't handle non child processes.
+ NOTIMPLEMENTED();
+#endif // OS_MACOSX
+ }
+
+ bool waitpid_success;
+ int status = -1;
+ if (wait.InMilliseconds() == base::kNoTimeout) {
+ waitpid_success = (HANDLE_EINTR(waitpid(handle, &status, 0)) != -1);
+ } else {
+ status = WaitpidWithTimeout(
+ handle, wait.InMilliseconds(), &waitpid_success);
+ }
+
+ if (status != -1) {
+ DCHECK(waitpid_success);
+ return WIFEXITED(status);
+ } else {
+ return false;
+ }
+}
+
+bool CleanupProcesses(const FilePath::StringType& executable_name,
+ base::TimeDelta wait,
+ int exit_code,
+ const ProcessFilter* filter) {
+ bool exited_cleanly = WaitForProcessesToExit(executable_name, wait, filter);
+ if (!exited_cleanly)
+ KillProcesses(executable_name, exit_code, filter);
+ return exited_cleanly;
+}
+
+#if !defined(OS_MACOSX)
+
+namespace {
+
+// Return true if the given child is dead. This will also reap the process.
+// Doesn't block.
+static bool IsChildDead(pid_t child) {
+ const pid_t result = HANDLE_EINTR(waitpid(child, NULL, WNOHANG));
+ if (result == -1) {
+ DPLOG(ERROR) << "waitpid(" << child << ")";
+ NOTREACHED();
+ } else if (result > 0) {
+ // The child has died.
+ return true;
+ }
+
+ return false;
+}
+
+// A thread class which waits for the given child to exit and reaps it.
+// If the child doesn't exit within a couple of seconds, kill it.
+class BackgroundReaper : public PlatformThread::Delegate {
+ public:
+ BackgroundReaper(pid_t child, unsigned timeout)
+ : child_(child),
+ timeout_(timeout) {
+ }
+
+ // Overridden from PlatformThread::Delegate:
+ virtual void ThreadMain() OVERRIDE {
+ WaitForChildToDie();
+ delete this;
+ }
+
+ void WaitForChildToDie() {
+ // Wait forever case.
+ if (timeout_ == 0) {
+ pid_t r = HANDLE_EINTR(waitpid(child_, NULL, 0));
+ if (r != child_) {
+ DPLOG(ERROR) << "While waiting for " << child_
+ << " to terminate, we got the following result: " << r;
+ }
+ return;
+ }
+
+ // There's no good way to wait for a specific child to exit in a timed
+ // fashion. (No kqueue on Linux), so we just loop and sleep.
+
+ // Wait for 2 * timeout_ 500 milliseconds intervals.
+ for (unsigned i = 0; i < 2 * timeout_; ++i) {
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(500));
+ if (IsChildDead(child_))
+ return;
+ }
+
+ if (kill(child_, SIGKILL) == 0) {
+ // SIGKILL is uncatchable. Since the signal was delivered, we can
+ // just wait for the process to die now in a blocking manner.
+ if (HANDLE_EINTR(waitpid(child_, NULL, 0)) < 0)
+ DPLOG(WARNING) << "waitpid";
+ } else {
+ DLOG(ERROR) << "While waiting for " << child_ << " to terminate we"
+ << " failed to deliver a SIGKILL signal (" << errno << ").";
+ }
+ }
+
+ private:
+ const pid_t child_;
+ // Number of seconds to wait, if 0 then wait forever and do not attempt to
+ // kill |child_|.
+ const unsigned timeout_;
+
+ DISALLOW_COPY_AND_ASSIGN(BackgroundReaper);
+};
+
+} // namespace
+
+void EnsureProcessTerminated(ProcessHandle process) {
+ // If the child is already dead, then there's nothing to do.
+ if (IsChildDead(process))
+ return;
+
+ const unsigned timeout = 2; // seconds
+ BackgroundReaper* reaper = new BackgroundReaper(process, timeout);
+ PlatformThread::CreateNonJoinable(0, reaper);
+}
+
+void EnsureProcessGetsReaped(ProcessHandle process) {
+ // If the child is already dead, then there's nothing to do.
+ if (IsChildDead(process))
+ return;
+
+ BackgroundReaper* reaper = new BackgroundReaper(process, 0);
+ PlatformThread::CreateNonJoinable(0, reaper);
+}
+
+#endif // !defined(OS_MACOSX)
+
+} // namespace base
diff --git a/base/process/kill_win.cc b/base/process/kill_win.cc
new file mode 100644
index 0000000..929ec4f
--- /dev/null
+++ b/base/process/kill_win.cc
@@ -0,0 +1,256 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/process/kill.h"
+
+#include <io.h>
+#include <windows.h>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/process/process_iterator.h"
+#include "base/process_util.h"
+#include "base/win/object_watcher.h"
+
+namespace base {
+
+namespace {
+
+// Exit codes with special meanings on Windows.
+const DWORD kNormalTerminationExitCode = 0;
+const DWORD kDebuggerInactiveExitCode = 0xC0000354;
+const DWORD kKeyboardInterruptExitCode = 0xC000013A;
+const DWORD kDebuggerTerminatedExitCode = 0x40010004;
+
+// This exit code is used by the Windows task manager when it kills a
+// process. It's value is obviously not that unique, and it's
+// surprising to me that the task manager uses this value, but it
+// seems to be common practice on Windows to test for it as an
+// indication that the task manager has killed something if the
+// process goes away.
+const DWORD kProcessKilledExitCode = 1;
+
+// Maximum amount of time (in milliseconds) to wait for the process to exit.
+static const int kWaitInterval = 2000;
+
+class TimerExpiredTask : public win::ObjectWatcher::Delegate {
+ public:
+ explicit TimerExpiredTask(ProcessHandle process);
+ ~TimerExpiredTask();
+
+ void TimedOut();
+
+ // MessageLoop::Watcher -----------------------------------------------------
+ virtual void OnObjectSignaled(HANDLE object);
+
+ private:
+ void KillProcess();
+
+ // The process that we are watching.
+ ProcessHandle process_;
+
+ win::ObjectWatcher watcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(TimerExpiredTask);
+};
+
+TimerExpiredTask::TimerExpiredTask(ProcessHandle process) : process_(process) {
+ watcher_.StartWatching(process_, this);
+}
+
+TimerExpiredTask::~TimerExpiredTask() {
+ TimedOut();
+ DCHECK(!process_) << "Make sure to close the handle.";
+}
+
+void TimerExpiredTask::TimedOut() {
+ if (process_)
+ KillProcess();
+}
+
+void TimerExpiredTask::OnObjectSignaled(HANDLE object) {
+ CloseHandle(process_);
+ process_ = NULL;
+}
+
+void TimerExpiredTask::KillProcess() {
+ // Stop watching the process handle since we're killing it.
+ watcher_.StopWatching();
+
+ // OK, time to get frisky. We don't actually care when the process
+ // terminates. We just care that it eventually terminates, and that's what
+ // TerminateProcess should do for us. Don't check for the result code since
+ // it fails quite often. This should be investigated eventually.
+ base::KillProcess(process_, kProcessKilledExitCode, false);
+
+ // Now, just cleanup as if the process exited normally.
+ OnObjectSignaled(process_);
+}
+
+} // namespace
+
+bool KillProcess(ProcessHandle process, int exit_code, bool wait) {
+ bool result = (TerminateProcess(process, exit_code) != FALSE);
+ if (result && wait) {
+ // The process may not end immediately due to pending I/O
+ if (WAIT_OBJECT_0 != WaitForSingleObject(process, 60 * 1000))
+ DLOG_GETLASTERROR(ERROR) << "Error waiting for process exit";
+ } else if (!result) {
+ DLOG_GETLASTERROR(ERROR) << "Unable to terminate process";
+ }
+ return result;
+}
+
+// Attempts to kill the process identified by the given process
+// entry structure, giving it the specified exit code.
+// Returns true if this is successful, false otherwise.
+bool KillProcessById(ProcessId process_id, int exit_code, bool wait) {
+ HANDLE process = OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE,
+ FALSE, // Don't inherit handle
+ process_id);
+ if (!process) {
+ DLOG_GETLASTERROR(ERROR) << "Unable to open process " << process_id;
+ return false;
+ }
+ bool ret = KillProcess(process, exit_code, wait);
+ CloseHandle(process);
+ return ret;
+}
+
+TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) {
+ DWORD tmp_exit_code = 0;
+
+ if (!::GetExitCodeProcess(handle, &tmp_exit_code)) {
+ DLOG_GETLASTERROR(FATAL) << "GetExitCodeProcess() failed";
+ if (exit_code) {
+ // This really is a random number. We haven't received any
+ // information about the exit code, presumably because this
+ // process doesn't have permission to get the exit code, or
+ // because of some other cause for GetExitCodeProcess to fail
+ // (MSDN docs don't give the possible failure error codes for
+ // this function, so it could be anything). But we don't want
+ // to leave exit_code uninitialized, since that could cause
+ // random interpretations of the exit code. So we assume it
+ // terminated "normally" in this case.
+ *exit_code = kNormalTerminationExitCode;
+ }
+ // Assume the child has exited normally if we can't get the exit
+ // code.
+ return TERMINATION_STATUS_NORMAL_TERMINATION;
+ }
+ if (tmp_exit_code == STILL_ACTIVE) {
+ DWORD wait_result = WaitForSingleObject(handle, 0);
+ if (wait_result == WAIT_TIMEOUT) {
+ if (exit_code)
+ *exit_code = wait_result;
+ return TERMINATION_STATUS_STILL_RUNNING;
+ }
+
+ if (wait_result == WAIT_FAILED) {
+ DLOG_GETLASTERROR(ERROR) << "WaitForSingleObject() failed";
+ } else {
+ DCHECK_EQ(WAIT_OBJECT_0, wait_result);
+
+ // Strange, the process used 0x103 (STILL_ACTIVE) as exit code.
+ NOTREACHED();
+ }
+
+ return TERMINATION_STATUS_ABNORMAL_TERMINATION;
+ }
+
+ if (exit_code)
+ *exit_code = tmp_exit_code;
+
+ switch (tmp_exit_code) {
+ case kNormalTerminationExitCode:
+ return TERMINATION_STATUS_NORMAL_TERMINATION;
+ case kDebuggerInactiveExitCode: // STATUS_DEBUGGER_INACTIVE.
+ case kKeyboardInterruptExitCode: // Control-C/end session.
+ case kDebuggerTerminatedExitCode: // Debugger terminated process.
+ case kProcessKilledExitCode: // Task manager kill.
+ return TERMINATION_STATUS_PROCESS_WAS_KILLED;
+ default:
+ // All other exit codes indicate crashes.
+ return TERMINATION_STATUS_PROCESS_CRASHED;
+ }
+}
+
+bool WaitForExitCode(ProcessHandle handle, int* exit_code) {
+ bool success = WaitForExitCodeWithTimeout(
+ handle, exit_code, base::TimeDelta::FromMilliseconds(INFINITE));
+ CloseProcessHandle(handle);
+ return success;
+}
+
+bool WaitForExitCodeWithTimeout(ProcessHandle handle,
+ int* exit_code,
+ base::TimeDelta timeout) {
+ if (::WaitForSingleObject(handle, timeout.InMilliseconds()) != WAIT_OBJECT_0)
+ return false;
+ DWORD temp_code; // Don't clobber out-parameters in case of failure.
+ if (!::GetExitCodeProcess(handle, &temp_code))
+ return false;
+
+ *exit_code = temp_code;
+ return true;
+}
+
+bool WaitForProcessesToExit(const FilePath::StringType& executable_name,
+ base::TimeDelta wait,
+ const ProcessFilter* filter) {
+ const ProcessEntry* entry;
+ bool result = true;
+ DWORD start_time = GetTickCount();
+
+ NamedProcessIterator iter(executable_name, filter);
+ while ((entry = iter.NextProcessEntry())) {
+ DWORD remaining_wait = std::max<int64>(
+ 0, wait.InMilliseconds() - (GetTickCount() - start_time));
+ HANDLE process = OpenProcess(SYNCHRONIZE,
+ FALSE,
+ entry->th32ProcessID);
+ DWORD wait_result = WaitForSingleObject(process, remaining_wait);
+ CloseHandle(process);
+ result = result && (wait_result == WAIT_OBJECT_0);
+ }
+
+ return result;
+}
+
+bool WaitForSingleProcess(ProcessHandle handle, base::TimeDelta wait) {
+ int exit_code;
+ if (!WaitForExitCodeWithTimeout(handle, &exit_code, wait))
+ return false;
+ return exit_code == 0;
+}
+
+bool CleanupProcesses(const FilePath::StringType& executable_name,
+ base::TimeDelta wait,
+ int exit_code,
+ const ProcessFilter* filter) {
+ bool exited_cleanly = WaitForProcessesToExit(executable_name, wait, filter);
+ if (!exited_cleanly)
+ KillProcesses(executable_name, exit_code, filter);
+ return exited_cleanly;
+}
+
+void EnsureProcessTerminated(ProcessHandle process) {
+ DCHECK(process != GetCurrentProcess());
+
+ // If already signaled, then we are done!
+ if (WaitForSingleObject(process, 0) == WAIT_OBJECT_0) {
+ CloseHandle(process);
+ return;
+ }
+
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&TimerExpiredTask::TimedOut,
+ base::Owned(new TimerExpiredTask(process))),
+ base::TimeDelta::FromMilliseconds(kWaitInterval));
+}
+
+} // namespace base
diff --git a/base/process_util.h b/base/process_util.h
index fdc9cf9..08add29 100644
--- a/base/process_util.h
+++ b/base/process_util.h
@@ -37,6 +37,7 @@ typedef struct _malloc_zone_t malloc_zone_t;
#include "base/files/file_path.h"
#include "base/process.h"
#include "base/process/memory.h"
+#include "base/process/kill.h"
#include "base/process/process_iterator.h"
#include "base/process/process_metrics.h"
@@ -48,18 +49,6 @@ class CommandLine;
namespace base {
-// Return status values from GetTerminationStatus. Don't use these as
-// exit code arguments to KillProcess*(), use platform/application
-// specific values instead.
-enum TerminationStatus {
- TERMINATION_STATUS_NORMAL_TERMINATION, // zero exit status
- TERMINATION_STATUS_ABNORMAL_TERMINATION, // non-zero exit status
- TERMINATION_STATUS_PROCESS_WAS_KILLED, // e.g. SIGKILL or task manager kill
- TERMINATION_STATUS_PROCESS_CRASHED, // e.g. Segmentation fault
- TERMINATION_STATUS_STILL_RUNNING, // child hasn't exited yet
- TERMINATION_STATUS_MAX_ENUM
-};
-
#if defined(OS_WIN)
// Output multi-process printf, cout, cerr, etc to the cmd.exe console that ran
// chrome. This is not thread-safe: only call from main thread.
@@ -345,115 +334,6 @@ BASE_EXPORT bool GetAppOutputWithExitCode(const CommandLine& cl,
std::string* output, int* exit_code);
#endif // defined(OS_POSIX)
-// Attempts to kill all the processes on the current machine that were launched
-// from the given executable name, ending them with the given exit code. If
-// filter is non-null, then only processes selected by the filter are killed.
-// Returns true if all processes were able to be killed off, false if at least
-// one couldn't be killed.
-BASE_EXPORT bool KillProcesses(const FilePath::StringType& executable_name,
- int exit_code, const ProcessFilter* filter);
-
-// Attempts to kill the process identified by the given process
-// entry structure, giving it the specified exit code. If |wait| is true, wait
-// for the process to be actually terminated before returning.
-// Returns true if this is successful, false otherwise.
-BASE_EXPORT bool KillProcess(ProcessHandle process, int exit_code, bool wait);
-
-#if defined(OS_POSIX)
-// Attempts to kill the process group identified by |process_group_id|. Returns
-// true on success.
-BASE_EXPORT bool KillProcessGroup(ProcessHandle process_group_id);
-#endif // defined(OS_POSIX)
-
-#if defined(OS_WIN)
-BASE_EXPORT bool KillProcessById(ProcessId process_id, int exit_code,
- bool wait);
-#endif // defined(OS_WIN)
-
-// Get the termination status of the process by interpreting the
-// circumstances of the child process' death. |exit_code| is set to
-// the status returned by waitpid() on POSIX, and from
-// GetExitCodeProcess() on Windows. |exit_code| may be NULL if the
-// caller is not interested in it. Note that on Linux, this function
-// will only return a useful result the first time it is called after
-// the child exits (because it will reap the child and the information
-// will no longer be available).
-BASE_EXPORT TerminationStatus GetTerminationStatus(ProcessHandle handle,
- int* exit_code);
-
-#if defined(OS_POSIX)
-// Wait for the process to exit and get the termination status. See
-// GetTerminationStatus for more information. On POSIX systems, we can't call
-// WaitForExitCode and then GetTerminationStatus as the child will be reaped
-// when WaitForExitCode return and this information will be lost.
-BASE_EXPORT TerminationStatus WaitForTerminationStatus(ProcessHandle handle,
- int* exit_code);
-#endif // defined(OS_POSIX)
-
-// Waits for process to exit. On POSIX systems, if the process hasn't been
-// signaled then puts the exit code in |exit_code|; otherwise it's considered
-// a failure. On Windows |exit_code| is always filled. Returns true on success,
-// and closes |handle| in any case.
-BASE_EXPORT bool WaitForExitCode(ProcessHandle handle, int* exit_code);
-
-// Waits for process to exit. If it did exit within |timeout_milliseconds|,
-// then puts the exit code in |exit_code|, and returns true.
-// In POSIX systems, if the process has been signaled then |exit_code| is set
-// to -1. Returns false on failure (the caller is then responsible for closing
-// |handle|).
-// The caller is always responsible for closing the |handle|.
-BASE_EXPORT bool WaitForExitCodeWithTimeout(ProcessHandle handle,
- int* exit_code,
- base::TimeDelta timeout);
-
-// Wait for all the processes based on the named executable to exit. If filter
-// is non-null, then only processes selected by the filter are waited on.
-// Returns after all processes have exited or wait_milliseconds have expired.
-// Returns true if all the processes exited, false otherwise.
-BASE_EXPORT bool WaitForProcessesToExit(
- const FilePath::StringType& executable_name,
- base::TimeDelta wait,
- const ProcessFilter* filter);
-
-// Wait for a single process to exit. Return true if it exited cleanly within
-// the given time limit. On Linux |handle| must be a child process, however
-// on Mac and Windows it can be any process.
-BASE_EXPORT bool WaitForSingleProcess(ProcessHandle handle,
- base::TimeDelta wait);
-
-// Waits a certain amount of time (can be 0) for all the processes with a given
-// executable name to exit, then kills off any of them that are still around.
-// If filter is non-null, then only processes selected by the filter are waited
-// on. Killed processes are ended with the given exit code. Returns false if
-// any processes needed to be killed, true if they all exited cleanly within
-// the wait_milliseconds delay.
-BASE_EXPORT bool CleanupProcesses(const FilePath::StringType& executable_name,
- base::TimeDelta wait,
- int exit_code,
- const ProcessFilter* filter);
-
-// This method ensures that the specified process eventually terminates, and
-// then it closes the given process handle.
-//
-// It assumes that the process has already been signalled to exit, and it
-// begins by waiting a small amount of time for it to exit. If the process
-// does not appear to have exited, then this function starts to become
-// aggressive about ensuring that the process terminates.
-//
-// On Linux this method does not block the calling thread.
-// On OS X this method may block for up to 2 seconds.
-//
-// NOTE: The process handle must have been opened with the PROCESS_TERMINATE
-// and SYNCHRONIZE permissions.
-//
-BASE_EXPORT void EnsureProcessTerminated(ProcessHandle process_handle);
-
-#if defined(OS_POSIX) && !defined(OS_MACOSX)
-// The nicer version of EnsureProcessTerminated() that is patient and will
-// wait for |process_handle| to finish and then reap it.
-BASE_EXPORT void EnsureProcessGetsReaped(ProcessHandle process_handle);
-#endif
-
// If supported on the platform, and the user has sufficent rights, increase
// the current process's scheduling priority to a high priority.
BASE_EXPORT void RaiseProcessToHighPriority();
diff --git a/base/process_util_mac.mm b/base/process_util_mac.mm
index 183463e..c82cd50 100644
--- a/base/process_util_mac.mm
+++ b/base/process_util_mac.mm
@@ -70,159 +70,4 @@ ProcessId GetParentProcessId(ProcessHandle process) {
return info.kp_eproc.e_ppid;
}
-namespace {
-
-const int kWaitBeforeKillSeconds = 2;
-
-// Reap |child| process. This call blocks until completion.
-void BlockingReap(pid_t child) {
- const pid_t result = HANDLE_EINTR(waitpid(child, NULL, 0));
- if (result == -1) {
- DPLOG(ERROR) << "waitpid(" << child << ", NULL, 0)";
- }
-}
-
-// Waits for |timeout| seconds for the given |child| to exit and reap it. If
-// the child doesn't exit within the time specified, kills it.
-//
-// This function takes two approaches: first, it tries to use kqueue to
-// observe when the process exits. kevent can monitor a kqueue with a
-// timeout, so this method is preferred to wait for a specified period of
-// time. Once the kqueue indicates the process has exited, waitpid will reap
-// the exited child. If the kqueue doesn't provide an exit event notification,
-// before the timeout expires, or if the kqueue fails or misbehaves, the
-// process will be mercilessly killed and reaped.
-//
-// A child process passed to this function may be in one of several states:
-// running, terminated and not yet reaped, and (apparently, and unfortunately)
-// terminated and already reaped. Normally, a process will at least have been
-// asked to exit before this function is called, but this is not required.
-// If a process is terminating and unreaped, there may be a window between the
-// time that kqueue will no longer recognize it and when it becomes an actual
-// zombie that a non-blocking (WNOHANG) waitpid can reap. This condition is
-// detected when kqueue indicates that the process is not running and a
-// non-blocking waitpid fails to reap the process but indicates that it is
-// still running. In this event, a blocking attempt to reap the process
-// collects the known-dying child, preventing zombies from congregating.
-//
-// In the event that the kqueue misbehaves entirely, as it might under a
-// EMFILE condition ("too many open files", or out of file descriptors), this
-// function will forcibly kill and reap the child without delay. This
-// eliminates another potential zombie vector. (If you're out of file
-// descriptors, you're probably deep into something else, but that doesn't
-// mean that zombies be allowed to kick you while you're down.)
-//
-// The fact that this function seemingly can be called to wait on a child
-// that's not only already terminated but already reaped is a bit of a
-// problem: a reaped child's pid can be reclaimed and may refer to a distinct
-// process in that case. The fact that this function can seemingly be called
-// to wait on a process that's not even a child is also a problem: kqueue will
-// work in that case, but waitpid won't, and killing a non-child might not be
-// the best approach.
-void WaitForChildToDie(pid_t child, int timeout) {
- DCHECK(child > 0);
- DCHECK(timeout > 0);
-
- // DON'T ADD ANY EARLY RETURNS TO THIS FUNCTION without ensuring that
- // |child| has been reaped. Specifically, even if a kqueue, kevent, or other
- // call fails, this function should fall back to the last resort of trying
- // to kill and reap the process. Not observing this rule will resurrect
- // zombies.
-
- int result;
-
- int kq = HANDLE_EINTR(kqueue());
- if (kq == -1) {
- DPLOG(ERROR) << "kqueue()";
- } else {
- file_util::ScopedFD auto_close_kq(&kq);
-
- struct kevent change = {0};
- EV_SET(&change, child, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
- result = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL));
-
- if (result == -1) {
- if (errno != ESRCH) {
- DPLOG(ERROR) << "kevent (setup " << child << ")";
- } else {
- // At this point, one of the following has occurred:
- // 1. The process has died but has not yet been reaped.
- // 2. The process has died and has already been reaped.
- // 3. The process is in the process of dying. It's no longer
- // kqueueable, but it may not be waitable yet either. Mark calls
- // this case the "zombie death race".
-
- result = HANDLE_EINTR(waitpid(child, NULL, WNOHANG));
-
- if (result != 0) {
- // A positive result indicates case 1. waitpid succeeded and reaped
- // the child. A result of -1 indicates case 2. The child has already
- // been reaped. In both of these cases, no further action is
- // necessary.
- return;
- }
-
- // |result| is 0, indicating case 3. The process will be waitable in
- // short order. Fall back out of the kqueue code to kill it (for good
- // measure) and reap it.
- }
- } else {
- // Keep track of the elapsed time to be able to restart kevent if it's
- // interrupted.
- TimeDelta remaining_delta = TimeDelta::FromSeconds(timeout);
- TimeTicks deadline = TimeTicks::Now() + remaining_delta;
- result = -1;
- struct kevent event = {0};
- while (remaining_delta.InMilliseconds() > 0) {
- const struct timespec remaining_timespec = remaining_delta.ToTimeSpec();
- result = kevent(kq, NULL, 0, &event, 1, &remaining_timespec);
- if (result == -1 && errno == EINTR) {
- remaining_delta = deadline - TimeTicks::Now();
- result = 0;
- } else {
- break;
- }
- }
-
- if (result == -1) {
- DPLOG(ERROR) << "kevent (wait " << child << ")";
- } else if (result > 1) {
- DLOG(ERROR) << "kevent (wait " << child << "): unexpected result "
- << result;
- } else if (result == 1) {
- if ((event.fflags & NOTE_EXIT) &&
- (event.ident == static_cast<uintptr_t>(child))) {
- // The process is dead or dying. This won't block for long, if at
- // all.
- BlockingReap(child);
- return;
- } else {
- DLOG(ERROR) << "kevent (wait " << child
- << "): unexpected event: fflags=" << event.fflags
- << ", ident=" << event.ident;
- }
- }
- }
- }
-
- // The child is still alive, or is very freshly dead. Be sure by sending it
- // a signal. This is safe even if it's freshly dead, because it will be a
- // zombie (or on the way to zombiedom) and kill will return 0 even if the
- // signal is not delivered to a live process.
- result = kill(child, SIGKILL);
- if (result == -1) {
- DPLOG(ERROR) << "kill(" << child << ", SIGKILL)";
- } else {
- // The child is definitely on the way out now. BlockingReap won't need to
- // wait for long, if at all.
- BlockingReap(child);
- }
-}
-
-} // namespace
-
-void EnsureProcessTerminated(ProcessHandle process) {
- WaitForChildToDie(process, kWaitBeforeKillSeconds);
-}
-
} // namespace base
diff --git a/base/process_util_posix.cc b/base/process_util_posix.cc
index 83afe44..e0a9169 100644
--- a/base/process_util_posix.cc
+++ b/base/process_util_posix.cc
@@ -74,67 +74,6 @@ void SetEnvironment(char** env) {
#endif
}
-int WaitpidWithTimeout(ProcessHandle handle, int64 wait_milliseconds,
- bool* success) {
- // This POSIX version of this function only guarantees that we wait no less
- // than |wait_milliseconds| for the process to exit. The child process may
- // exit sometime before the timeout has ended but we may still block for up
- // to 256 milliseconds after the fact.
- //
- // waitpid() has no direct support on POSIX for specifying a timeout, you can
- // either ask it to block indefinitely or return immediately (WNOHANG).
- // When a child process terminates a SIGCHLD signal is sent to the parent.
- // Catching this signal would involve installing a signal handler which may
- // affect other parts of the application and would be difficult to debug.
- //
- // Our strategy is to call waitpid() once up front to check if the process
- // has already exited, otherwise to loop for wait_milliseconds, sleeping for
- // at most 256 milliseconds each time using usleep() and then calling
- // waitpid(). The amount of time we sleep starts out at 1 milliseconds, and
- // we double it every 4 sleep cycles.
- //
- // usleep() is speced to exit if a signal is received for which a handler
- // has been installed. This means that when a SIGCHLD is sent, it will exit
- // depending on behavior external to this function.
- //
- // This function is used primarily for unit tests, if we want to use it in
- // the application itself it would probably be best to examine other routes.
- int status = -1;
- pid_t ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG));
- static const int64 kMaxSleepInMicroseconds = 1 << 18; // ~256 milliseconds.
- int64 max_sleep_time_usecs = 1 << 10; // ~1 milliseconds.
- int64 double_sleep_time = 0;
-
- // If the process hasn't exited yet, then sleep and try again.
- TimeTicks wakeup_time = TimeTicks::Now() +
- TimeDelta::FromMilliseconds(wait_milliseconds);
- while (ret_pid == 0) {
- TimeTicks now = TimeTicks::Now();
- if (now > wakeup_time)
- break;
- // Guaranteed to be non-negative!
- int64 sleep_time_usecs = (wakeup_time - now).InMicroseconds();
- // Sleep for a bit while we wait for the process to finish.
- if (sleep_time_usecs > max_sleep_time_usecs)
- sleep_time_usecs = max_sleep_time_usecs;
-
- // usleep() will return 0 and set errno to EINTR on receipt of a signal
- // such as SIGCHLD.
- usleep(sleep_time_usecs);
- ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG));
-
- if ((max_sleep_time_usecs < kMaxSleepInMicroseconds) &&
- (double_sleep_time++ % 4 == 0)) {
- max_sleep_time_usecs *= 2;
- }
- }
-
- if (success)
- *success = (ret_pid != -1);
-
- return status;
-}
-
#if !defined(OS_LINUX) || \
(!defined(__i386__) && !defined(__x86_64__) && !defined(__arm__))
void ResetChildSignalHandlersToDefaults() {
@@ -221,50 +160,6 @@ void ResetChildSignalHandlersToDefaults(void) {
#endif // !defined(OS_LINUX) ||
// (!defined(__i386__) && !defined(__x86_64__) && !defined(__arm__))
-TerminationStatus GetTerminationStatusImpl(ProcessHandle handle,
- bool can_block,
- int* exit_code) {
- int status = 0;
- const pid_t result = HANDLE_EINTR(waitpid(handle, &status,
- can_block ? 0 : WNOHANG));
- if (result == -1) {
- DPLOG(ERROR) << "waitpid(" << handle << ")";
- if (exit_code)
- *exit_code = 0;
- return TERMINATION_STATUS_NORMAL_TERMINATION;
- } else if (result == 0) {
- // the child hasn't exited yet.
- if (exit_code)
- *exit_code = 0;
- return TERMINATION_STATUS_STILL_RUNNING;
- }
-
- if (exit_code)
- *exit_code = status;
-
- if (WIFSIGNALED(status)) {
- switch (WTERMSIG(status)) {
- case SIGABRT:
- case SIGBUS:
- case SIGFPE:
- case SIGILL:
- case SIGSEGV:
- return TERMINATION_STATUS_PROCESS_CRASHED;
- case SIGINT:
- case SIGKILL:
- case SIGTERM:
- return TERMINATION_STATUS_PROCESS_WAS_KILLED;
- default:
- break;
- }
- }
-
- if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
- return TERMINATION_STATUS_ABNORMAL_TERMINATION;
-
- return TERMINATION_STATUS_NORMAL_TERMINATION;
-}
-
} // anonymous namespace
ProcessId GetCurrentProcId() {
@@ -305,68 +200,6 @@ ProcessId GetProcId(ProcessHandle process) {
return process;
}
-// Attempts to kill the process identified by the given process
-// entry structure. Ignores specified exit_code; posix can't force that.
-// Returns true if this is successful, false otherwise.
-bool KillProcess(ProcessHandle process_id, int exit_code, bool wait) {
- DCHECK_GT(process_id, 1) << " tried to kill invalid process_id";
- if (process_id <= 1)
- return false;
- bool result = kill(process_id, SIGTERM) == 0;
- if (result && wait) {
- int tries = 60;
-
- if (RunningOnValgrind()) {
- // Wait for some extra time when running under Valgrind since the child
- // processes may take some time doing leak checking.
- tries *= 2;
- }
-
- unsigned sleep_ms = 4;
-
- // The process may not end immediately due to pending I/O
- bool exited = false;
- while (tries-- > 0) {
- pid_t pid = HANDLE_EINTR(waitpid(process_id, NULL, WNOHANG));
- if (pid == process_id) {
- exited = true;
- break;
- }
- if (pid == -1) {
- if (errno == ECHILD) {
- // The wait may fail with ECHILD if another process also waited for
- // the same pid, causing the process state to get cleaned up.
- exited = true;
- break;
- }
- DPLOG(ERROR) << "Error waiting for process " << process_id;
- }
-
- usleep(sleep_ms * 1000);
- const unsigned kMaxSleepMs = 1000;
- if (sleep_ms < kMaxSleepMs)
- sleep_ms *= 2;
- }
-
- // If we're waiting and the child hasn't died by now, force it
- // with a SIGKILL.
- if (!exited)
- result = kill(process_id, SIGKILL) == 0;
- }
-
- if (!result)
- DPLOG(ERROR) << "Unable to terminate process " << process_id;
-
- return result;
-}
-
-bool KillProcessGroup(ProcessHandle process_group_id) {
- bool result = kill(-1 * process_group_id, SIGKILL) == 0;
- if (!result)
- DPLOG(ERROR) << "Unable to terminate process group " << process_group_id;
- return result;
-}
-
// A class to handle auto-closing of DIR*'s.
class ScopedDIRClose {
public:
@@ -776,174 +609,6 @@ void RaiseProcessToHighPriority() {
// setpriority() or sched_getscheduler, but these all require extra rights.
}
-TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) {
- return GetTerminationStatusImpl(handle, false /* can_block */, exit_code);
-}
-
-TerminationStatus WaitForTerminationStatus(ProcessHandle handle,
- int* exit_code) {
- return GetTerminationStatusImpl(handle, true /* can_block */, exit_code);
-}
-
-bool WaitForExitCode(ProcessHandle handle, int* exit_code) {
- int status;
- if (HANDLE_EINTR(waitpid(handle, &status, 0)) == -1) {
- NOTREACHED();
- return false;
- }
-
- if (WIFEXITED(status)) {
- *exit_code = WEXITSTATUS(status);
- return true;
- }
-
- // If it didn't exit cleanly, it must have been signaled.
- DCHECK(WIFSIGNALED(status));
- return false;
-}
-
-bool WaitForExitCodeWithTimeout(ProcessHandle handle, int* exit_code,
- base::TimeDelta timeout) {
- bool waitpid_success = false;
- int status = WaitpidWithTimeout(handle, timeout.InMilliseconds(),
- &waitpid_success);
- if (status == -1)
- return false;
- if (!waitpid_success)
- return false;
- if (WIFSIGNALED(status)) {
- *exit_code = -1;
- return true;
- }
- if (WIFEXITED(status)) {
- *exit_code = WEXITSTATUS(status);
- return true;
- }
- return false;
-}
-
-#if defined(OS_MACOSX)
-// Using kqueue on Mac so that we can wait on non-child processes.
-// We can't use kqueues on child processes because we need to reap
-// our own children using wait.
-static bool WaitForSingleNonChildProcess(ProcessHandle handle,
- base::TimeDelta wait) {
- DCHECK_GT(handle, 0);
- DCHECK(wait.InMilliseconds() == base::kNoTimeout || wait > base::TimeDelta());
-
- int kq = kqueue();
- if (kq == -1) {
- DPLOG(ERROR) << "kqueue";
- return false;
- }
- file_util::ScopedFD kq_closer(&kq);
-
- struct kevent change = {0};
- EV_SET(&change, handle, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
- int result = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL));
- if (result == -1) {
- if (errno == ESRCH) {
- // If the process wasn't found, it must be dead.
- return true;
- }
-
- DPLOG(ERROR) << "kevent (setup " << handle << ")";
- return false;
- }
-
- // Keep track of the elapsed time to be able to restart kevent if it's
- // interrupted.
- bool wait_forever = wait.InMilliseconds() == base::kNoTimeout;
- base::TimeDelta remaining_delta;
- base::TimeTicks deadline;
- if (!wait_forever) {
- remaining_delta = wait;
- deadline = base::TimeTicks::Now() + remaining_delta;
- }
-
- result = -1;
- struct kevent event = {0};
-
- while (wait_forever || remaining_delta > base::TimeDelta()) {
- struct timespec remaining_timespec;
- struct timespec* remaining_timespec_ptr;
- if (wait_forever) {
- remaining_timespec_ptr = NULL;
- } else {
- remaining_timespec = remaining_delta.ToTimeSpec();
- remaining_timespec_ptr = &remaining_timespec;
- }
-
- result = kevent(kq, NULL, 0, &event, 1, remaining_timespec_ptr);
-
- if (result == -1 && errno == EINTR) {
- if (!wait_forever) {
- remaining_delta = deadline - base::TimeTicks::Now();
- }
- result = 0;
- } else {
- break;
- }
- }
-
- if (result < 0) {
- DPLOG(ERROR) << "kevent (wait " << handle << ")";
- return false;
- } else if (result > 1) {
- DLOG(ERROR) << "kevent (wait " << handle << "): unexpected result "
- << result;
- return false;
- } else if (result == 0) {
- // Timed out.
- return false;
- }
-
- DCHECK_EQ(result, 1);
-
- if (event.filter != EVFILT_PROC ||
- (event.fflags & NOTE_EXIT) == 0 ||
- event.ident != static_cast<uintptr_t>(handle)) {
- DLOG(ERROR) << "kevent (wait " << handle
- << "): unexpected event: filter=" << event.filter
- << ", fflags=" << event.fflags
- << ", ident=" << event.ident;
- return false;
- }
-
- return true;
-}
-#endif // OS_MACOSX
-
-bool WaitForSingleProcess(ProcessHandle handle, base::TimeDelta wait) {
- ProcessHandle parent_pid = GetParentProcessId(handle);
- ProcessHandle our_pid = Process::Current().handle();
- if (parent_pid != our_pid) {
-#if defined(OS_MACOSX)
- // On Mac we can wait on non child processes.
- return WaitForSingleNonChildProcess(handle, wait);
-#else
- // Currently on Linux we can't handle non child processes.
- NOTIMPLEMENTED();
-#endif // OS_MACOSX
- }
-
- bool waitpid_success;
- int status = -1;
- if (wait.InMilliseconds() == base::kNoTimeout) {
- waitpid_success = (HANDLE_EINTR(waitpid(handle, &status, 0)) != -1);
- } else {
- status = WaitpidWithTimeout(
- handle, wait.InMilliseconds(), &waitpid_success);
- }
-
- if (status != -1) {
- DCHECK(waitpid_success);
- return WIFEXITED(status);
- } else {
- return false;
- }
-}
-
// Return value used by GetAppOutputInternal to encapsulate the various exit
// scenarios from the function.
enum GetAppOutputInternalResult {
@@ -1119,133 +784,4 @@ bool GetAppOutputWithExitCode(const CommandLine& cl,
return result == EXECUTE_SUCCESS;
}
-bool WaitForProcessesToExit(const FilePath::StringType& executable_name,
- base::TimeDelta wait,
- const ProcessFilter* filter) {
- bool result = false;
-
- // TODO(port): This is inefficient, but works if there are multiple procs.
- // TODO(port): use waitpid to avoid leaving zombies around
-
- base::TimeTicks end_time = base::TimeTicks::Now() + wait;
- do {
- NamedProcessIterator iter(executable_name, filter);
- if (!iter.NextProcessEntry()) {
- result = true;
- break;
- }
- base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
- } while ((end_time - base::TimeTicks::Now()) > base::TimeDelta());
-
- return result;
-}
-
-bool CleanupProcesses(const FilePath::StringType& executable_name,
- base::TimeDelta wait,
- int exit_code,
- const ProcessFilter* filter) {
- bool exited_cleanly = WaitForProcessesToExit(executable_name, wait, filter);
- if (!exited_cleanly)
- KillProcesses(executable_name, exit_code, filter);
- return exited_cleanly;
-}
-
-#if !defined(OS_MACOSX)
-
-namespace {
-
-// Return true if the given child is dead. This will also reap the process.
-// Doesn't block.
-static bool IsChildDead(pid_t child) {
- const pid_t result = HANDLE_EINTR(waitpid(child, NULL, WNOHANG));
- if (result == -1) {
- DPLOG(ERROR) << "waitpid(" << child << ")";
- NOTREACHED();
- } else if (result > 0) {
- // The child has died.
- return true;
- }
-
- return false;
-}
-
-// A thread class which waits for the given child to exit and reaps it.
-// If the child doesn't exit within a couple of seconds, kill it.
-class BackgroundReaper : public PlatformThread::Delegate {
- public:
- BackgroundReaper(pid_t child, unsigned timeout)
- : child_(child),
- timeout_(timeout) {
- }
-
- // Overridden from PlatformThread::Delegate:
- virtual void ThreadMain() OVERRIDE {
- WaitForChildToDie();
- delete this;
- }
-
- void WaitForChildToDie() {
- // Wait forever case.
- if (timeout_ == 0) {
- pid_t r = HANDLE_EINTR(waitpid(child_, NULL, 0));
- if (r != child_) {
- DPLOG(ERROR) << "While waiting for " << child_
- << " to terminate, we got the following result: " << r;
- }
- return;
- }
-
- // There's no good way to wait for a specific child to exit in a timed
- // fashion. (No kqueue on Linux), so we just loop and sleep.
-
- // Wait for 2 * timeout_ 500 milliseconds intervals.
- for (unsigned i = 0; i < 2 * timeout_; ++i) {
- PlatformThread::Sleep(TimeDelta::FromMilliseconds(500));
- if (IsChildDead(child_))
- return;
- }
-
- if (kill(child_, SIGKILL) == 0) {
- // SIGKILL is uncatchable. Since the signal was delivered, we can
- // just wait for the process to die now in a blocking manner.
- if (HANDLE_EINTR(waitpid(child_, NULL, 0)) < 0)
- DPLOG(WARNING) << "waitpid";
- } else {
- DLOG(ERROR) << "While waiting for " << child_ << " to terminate we"
- << " failed to deliver a SIGKILL signal (" << errno << ").";
- }
- }
-
- private:
- const pid_t child_;
- // Number of seconds to wait, if 0 then wait forever and do not attempt to
- // kill |child_|.
- const unsigned timeout_;
-
- DISALLOW_COPY_AND_ASSIGN(BackgroundReaper);
-};
-
-} // namespace
-
-void EnsureProcessTerminated(ProcessHandle process) {
- // If the child is already dead, then there's nothing to do.
- if (IsChildDead(process))
- return;
-
- const unsigned timeout = 2; // seconds
- BackgroundReaper* reaper = new BackgroundReaper(process, timeout);
- PlatformThread::CreateNonJoinable(0, reaper);
-}
-
-void EnsureProcessGetsReaped(ProcessHandle process) {
- // If the child is already dead, then there's nothing to do.
- if (IsChildDead(process))
- return;
-
- BackgroundReaper* reaper = new BackgroundReaper(process, 0);
- PlatformThread::CreateNonJoinable(0, reaper);
-}
-
-#endif // !defined(OS_MACOSX)
-
} // namespace base
diff --git a/base/process_util_win.cc b/base/process_util_win.cc
index 2230105..691e1ae 100644
--- a/base/process_util_win.cc
+++ b/base/process_util_win.cc
@@ -33,15 +33,6 @@ namespace base {
namespace {
-// Exit codes with special meanings on Windows.
-const DWORD kNormalTerminationExitCode = 0;
-const DWORD kDebuggerInactiveExitCode = 0xC0000354;
-const DWORD kKeyboardInterruptExitCode = 0xC000013A;
-const DWORD kDebuggerTerminatedExitCode = 0x40010004;
-
-// Maximum amount of time (in milliseconds) to wait for the process to exit.
-static const int kWaitInterval = 2000;
-
// This exit code is used by the Windows task manager when it kills a
// process. It's value is obviously not that unique, and it's
// surprising to me that the task manager uses this value, but it
@@ -50,60 +41,6 @@ static const int kWaitInterval = 2000;
// process goes away.
const DWORD kProcessKilledExitCode = 1;
-class TimerExpiredTask : public win::ObjectWatcher::Delegate {
- public:
- explicit TimerExpiredTask(ProcessHandle process);
- ~TimerExpiredTask();
-
- void TimedOut();
-
- // MessageLoop::Watcher -----------------------------------------------------
- virtual void OnObjectSignaled(HANDLE object);
-
- private:
- void KillProcess();
-
- // The process that we are watching.
- ProcessHandle process_;
-
- win::ObjectWatcher watcher_;
-
- DISALLOW_COPY_AND_ASSIGN(TimerExpiredTask);
-};
-
-TimerExpiredTask::TimerExpiredTask(ProcessHandle process) : process_(process) {
- watcher_.StartWatching(process_, this);
-}
-
-TimerExpiredTask::~TimerExpiredTask() {
- TimedOut();
- DCHECK(!process_) << "Make sure to close the handle.";
-}
-
-void TimerExpiredTask::TimedOut() {
- if (process_)
- KillProcess();
-}
-
-void TimerExpiredTask::OnObjectSignaled(HANDLE object) {
- CloseHandle(process_);
- process_ = NULL;
-}
-
-void TimerExpiredTask::KillProcess() {
- // Stop watching the process handle since we're killing it.
- watcher_.StopWatching();
-
- // OK, time to get frisky. We don't actually care when the process
- // terminates. We just care that it eventually terminates, and that's what
- // TerminateProcess should do for us. Don't check for the result code since
- // it fails quite often. This should be investigated eventually.
- base::KillProcess(process_, kProcessKilledExitCode, false);
-
- // Now, just cleanup as if the process exited normally.
- OnObjectSignaled(process_);
-}
-
} // namespace
void RouteStdioToConsole() {
@@ -379,22 +316,6 @@ bool SetJobObjectAsKillOnJobClose(HANDLE job_object) {
sizeof(limit_info));
}
-// Attempts to kill the process identified by the given process
-// entry structure, giving it the specified exit code.
-// Returns true if this is successful, false otherwise.
-bool KillProcessById(ProcessId process_id, int exit_code, bool wait) {
- HANDLE process = OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE,
- FALSE, // Don't inherit handle
- process_id);
- if (!process) {
- DLOG_GETLASTERROR(ERROR) << "Unable to open process " << process_id;
- return false;
- }
- bool ret = KillProcess(process, exit_code, wait);
- CloseHandle(process);
- return ret;
-}
-
bool GetAppOutput(const CommandLine& cl, std::string* output) {
HANDLE out_read = NULL;
HANDLE out_write = NULL;
@@ -465,150 +386,6 @@ bool GetAppOutput(const CommandLine& cl, std::string* output) {
return true;
}
-bool KillProcess(ProcessHandle process, int exit_code, bool wait) {
- bool result = (TerminateProcess(process, exit_code) != FALSE);
- if (result && wait) {
- // The process may not end immediately due to pending I/O
- if (WAIT_OBJECT_0 != WaitForSingleObject(process, 60 * 1000))
- DLOG_GETLASTERROR(ERROR) << "Error waiting for process exit";
- } else if (!result) {
- DLOG_GETLASTERROR(ERROR) << "Unable to terminate process";
- }
- return result;
-}
-
-TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) {
- DWORD tmp_exit_code = 0;
-
- if (!::GetExitCodeProcess(handle, &tmp_exit_code)) {
- DLOG_GETLASTERROR(FATAL) << "GetExitCodeProcess() failed";
- if (exit_code) {
- // This really is a random number. We haven't received any
- // information about the exit code, presumably because this
- // process doesn't have permission to get the exit code, or
- // because of some other cause for GetExitCodeProcess to fail
- // (MSDN docs don't give the possible failure error codes for
- // this function, so it could be anything). But we don't want
- // to leave exit_code uninitialized, since that could cause
- // random interpretations of the exit code. So we assume it
- // terminated "normally" in this case.
- *exit_code = kNormalTerminationExitCode;
- }
- // Assume the child has exited normally if we can't get the exit
- // code.
- return TERMINATION_STATUS_NORMAL_TERMINATION;
- }
- if (tmp_exit_code == STILL_ACTIVE) {
- DWORD wait_result = WaitForSingleObject(handle, 0);
- if (wait_result == WAIT_TIMEOUT) {
- if (exit_code)
- *exit_code = wait_result;
- return TERMINATION_STATUS_STILL_RUNNING;
- }
-
- if (wait_result == WAIT_FAILED) {
- DLOG_GETLASTERROR(ERROR) << "WaitForSingleObject() failed";
- } else {
- DCHECK_EQ(WAIT_OBJECT_0, wait_result);
-
- // Strange, the process used 0x103 (STILL_ACTIVE) as exit code.
- NOTREACHED();
- }
-
- return TERMINATION_STATUS_ABNORMAL_TERMINATION;
- }
-
- if (exit_code)
- *exit_code = tmp_exit_code;
-
- switch (tmp_exit_code) {
- case kNormalTerminationExitCode:
- return TERMINATION_STATUS_NORMAL_TERMINATION;
- case kDebuggerInactiveExitCode: // STATUS_DEBUGGER_INACTIVE.
- case kKeyboardInterruptExitCode: // Control-C/end session.
- case kDebuggerTerminatedExitCode: // Debugger terminated process.
- case kProcessKilledExitCode: // Task manager kill.
- return TERMINATION_STATUS_PROCESS_WAS_KILLED;
- default:
- // All other exit codes indicate crashes.
- return TERMINATION_STATUS_PROCESS_CRASHED;
- }
-}
-
-bool WaitForExitCode(ProcessHandle handle, int* exit_code) {
- bool success = WaitForExitCodeWithTimeout(
- handle, exit_code, base::TimeDelta::FromMilliseconds(INFINITE));
- CloseProcessHandle(handle);
- return success;
-}
-
-bool WaitForExitCodeWithTimeout(ProcessHandle handle, int* exit_code,
- base::TimeDelta timeout) {
- if (::WaitForSingleObject(handle, timeout.InMilliseconds()) != WAIT_OBJECT_0)
- return false;
- DWORD temp_code; // Don't clobber out-parameters in case of failure.
- if (!::GetExitCodeProcess(handle, &temp_code))
- return false;
-
- *exit_code = temp_code;
- return true;
-}
-
-bool WaitForProcessesToExit(const FilePath::StringType& executable_name,
- base::TimeDelta wait,
- const ProcessFilter* filter) {
- const ProcessEntry* entry;
- bool result = true;
- DWORD start_time = GetTickCount();
-
- NamedProcessIterator iter(executable_name, filter);
- while ((entry = iter.NextProcessEntry())) {
- DWORD remaining_wait = std::max<int64>(
- 0, wait.InMilliseconds() - (GetTickCount() - start_time));
- HANDLE process = OpenProcess(SYNCHRONIZE,
- FALSE,
- entry->th32ProcessID);
- DWORD wait_result = WaitForSingleObject(process, remaining_wait);
- CloseHandle(process);
- result = result && (wait_result == WAIT_OBJECT_0);
- }
-
- return result;
-}
-
-bool WaitForSingleProcess(ProcessHandle handle, base::TimeDelta wait) {
- int exit_code;
- if (!WaitForExitCodeWithTimeout(handle, &exit_code, wait))
- return false;
- return exit_code == 0;
-}
-
-bool CleanupProcesses(const FilePath::StringType& executable_name,
- base::TimeDelta wait,
- int exit_code,
- const ProcessFilter* filter) {
- bool exited_cleanly = WaitForProcessesToExit(executable_name, wait, filter);
- if (!exited_cleanly)
- KillProcesses(executable_name, exit_code, filter);
- return exited_cleanly;
-}
-
-void EnsureProcessTerminated(ProcessHandle process) {
- DCHECK(process != GetCurrentProcess());
-
- // If already signaled, then we are done!
- if (WaitForSingleObject(process, 0) == WAIT_OBJECT_0) {
- CloseHandle(process);
- return;
- }
-
- MessageLoop::current()->PostDelayedTask(
- FROM_HERE,
- base::Bind(&TimerExpiredTask::TimedOut,
- base::Owned(new TimerExpiredTask(process))),
- base::TimeDelta::FromMilliseconds(kWaitInterval));
-}
-
void RaiseProcessToHighPriority() {
SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
}