Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(216)

Unified Diff: base/test/test_launcher.cc

Issue 23484026: GTTF: kill spawned test processes when the launcher is killed (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: LAZY_INSTANCE_INITIALIZER Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | base/test/unit_test_launcher.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: base/test/test_launcher.cc
diff --git a/base/test/test_launcher.cc b/base/test/test_launcher.cc
index deabb9a4add1dc1b8ed692d08b5cb6466f4be403..25482b3929170fd84b60182abebd2c82d2d5cc14 100644
--- a/base/test/test_launcher.cc
+++ b/base/test/test_launcher.cc
@@ -4,6 +4,10 @@
#include "base/test/test_launcher.h"
+#if defined(OS_POSIX)
+#include <fcntl.h>
+#endif
+
#include "base/at_exit.h"
#include "base/bind.h"
#include "base/command_line.h"
@@ -11,6 +15,7 @@
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/format_macros.h"
+#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
@@ -44,6 +49,85 @@ const FilePath::CharType kDefaultOutputFile[] = FILE_PATH_LITERAL(
namespace {
+// Set of live launch test processes with corresponding lock (it is allowed
+// for callers to launch processes on different threads).
+LazyInstance<std::set<ProcessHandle> > g_live_process_handles
+ = LAZY_INSTANCE_INITIALIZER;
+LazyInstance<Lock> g_live_process_handles_lock = LAZY_INSTANCE_INITIALIZER;
+
+#if defined(OS_POSIX)
+// Self-pipe that makes it possible to do complex shutdown handling
+// outside of the signal handler.
+int g_shutdown_pipe[2] = { -1, -1 };
+
+void ShutdownPipeSignalHandler(int signal) {
+ HANDLE_EINTR(write(g_shutdown_pipe[1], "q", 1));
+}
+
+// I/O watcher for the reading end of the self-pipe above.
+// Terminates any launched child processes and exits the process.
+class SignalFDWatcher : public MessageLoopForIO::Watcher {
+ public:
+ SignalFDWatcher() {
+ }
+
+ virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE {
+ fprintf(stdout, "\nCaught signal. Killing spawned test processes...\n");
+ fflush(stdout);
+
+ // Keep the lock until exiting the process to prevent further processes
+ // from being spawned.
+ AutoLock lock(g_live_process_handles_lock.Get());
+
+ fprintf(stdout,
+ "Sending SIGTERM to %" PRIuS " child processes... ",
+ g_live_process_handles.Get().size());
+ fflush(stdout);
+
+ for (std::set<ProcessHandle>::iterator i =
+ g_live_process_handles.Get().begin();
+ i != g_live_process_handles.Get().end();
+ ++i) {
+ kill((-1) * (*i), SIGTERM); // Send the signal to entire process group.
+ }
+
+ fprintf(stdout,
+ "done.\nGiving processes a chance to terminate cleanly... ");
+ fflush(stdout);
+
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(500));
+
+ fprintf(stdout, "done.\n");
+ fflush(stdout);
+
+ fprintf(stdout,
+ "Sending SIGKILL to %" PRIuS " child processes... ",
+ g_live_process_handles.Get().size());
+ fflush(stdout);
+
+ for (std::set<ProcessHandle>::iterator i =
+ g_live_process_handles.Get().begin();
+ i != g_live_process_handles.Get().end();
+ ++i) {
+ kill((-1) * (*i), SIGKILL); // Send the signal to entire process group.
+ }
+
+ fprintf(stdout, "done.\n");
+ fflush(stdout);
+
+ // The signal would normally kill the process, so exit now.
+ exit(1);
+ }
+
+ virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE {
+ NOTREACHED();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SignalFDWatcher);
+};
+#endif // defined(OS_POSIX)
+
// Parses the environment variable var as an Int32. If it is unset, returns
// default_val. If it is set, unsets it then converts it to Int32 before
// returning it. If unsetting or converting to an Int32 fails, print an
@@ -541,8 +625,18 @@ int LaunchChildTestProcessWithOptions(const CommandLine& command_line,
#endif
base::ProcessHandle process_handle;
- if (!base::LaunchProcess(command_line, options, &process_handle))
- return -1;
+
+ {
+ // Note how we grab the lock before the process possibly gets created.
+ // This ensures that when the lock is held, ALL the processes are registered
+ // in the set.
+ AutoLock lock(g_live_process_handles_lock.Get());
+
+ if (!base::LaunchProcess(command_line, options, &process_handle))
+ return -1;
+
+ g_live_process_handles.Get().insert(process_handle);
+ }
int exit_code = 0;
if (!base::WaitForExitCodeWithTimeout(process_handle,
@@ -555,16 +649,25 @@ int LaunchChildTestProcessWithOptions(const CommandLine& command_line,
base::KillProcess(process_handle, -1, true);
}
+ {
+ // Note how we grab the log before issuing a possibly broad process kill.
+ // Other code parts that grab the log kill processes, so avoid trying
+ // to do that twice and trigger all kinds of log messages.
+ AutoLock lock(g_live_process_handles_lock.Get());
+
#if defined(OS_POSIX)
- if (exit_code != 0) {
- // On POSIX, in case the test does not exit cleanly, either due to a crash
- // or due to it timing out, we need to clean up any child processes that
- // it might have created. On Windows, child processes are automatically
- // cleaned up using JobObjects.
- base::KillProcessGroup(process_handle);
- }
+ if (exit_code != 0) {
+ // On POSIX, in case the test does not exit cleanly, either due to a crash
+ // or due to it timing out, we need to clean up any child processes that
+ // it might have created. On Windows, child processes are automatically
+ // cleaned up using JobObjects.
+ base::KillProcessGroup(process_handle);
+ }
#endif
+ g_live_process_handles.Get().erase(process_handle);
+ }
+
base::CloseProcessHandle(process_handle);
return exit_code;
@@ -586,6 +689,29 @@ int LaunchTests(TestLauncherDelegate* launcher_delegate,
int exit_code = 0;
+#if defined(OS_POSIX)
+ CHECK_EQ(0, pipe(g_shutdown_pipe));
+
+ struct sigaction action;
+ memset(&action, 0, sizeof(action));
+ sigemptyset(&action.sa_mask);
+ action.sa_handler = &ShutdownPipeSignalHandler;
+
+ CHECK_EQ(0, sigaction(SIGINT, &action, NULL));
+ CHECK_EQ(0, sigaction(SIGQUIT, &action, NULL));
+ CHECK_EQ(0, sigaction(SIGTERM, &action, NULL));
+
+ MessageLoopForIO::FileDescriptorWatcher controller;
+ SignalFDWatcher watcher;
+
+ CHECK(MessageLoopForIO::current()->WatchFileDescriptor(
+ g_shutdown_pipe[0],
+ true,
+ MessageLoopForIO::WATCH_READ,
+ &controller,
+ &watcher));
+#endif // defined(OS_POSIX)
+
MessageLoop::current()->PostTask(
FROM_HERE,
Bind(&RunTestIteration,
« no previous file with comments | « no previous file | base/test/unit_test_launcher.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698