process_singleton_browsertest.cc 13.5 KB
Newer Older
1
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 3 4 5 6 7
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This test validates that the ProcessSingleton class properly makes sure
// that there is only one main browser process.
//
8 9 10 11
// It is currently compiled and run on Windows and Posix(non-Mac) platforms.
// Mac uses system services and ProcessSingletonMac is a noop.  (Maybe it still
// makes sense to test that the system services are giving the behavior we
// want?)
12

13
#include "base/bind.h"
14
#include "base/command_line.h"
15
#include "base/files/file_path.h"
16
#include "base/files/scoped_temp_dir.h"
17
#include "base/memory/ref_counted.h"
18
#include "base/path_service.h"
19 20 21
#include "base/process/kill.h"
#include "base/process/launch.h"
#include "base/process/process_iterator.h"
22
#include "base/synchronization/waitable_event.h"
23 24
#include "base/test/test_timeouts.h"
#include "base/threading/thread.h"
25
#include "chrome/common/chrome_constants.h"
26
#include "chrome/common/chrome_paths.h"
27
#include "chrome/common/chrome_switches.h"
28
#include "chrome/test/base/in_process_browser_test.h"
29
#include "chrome/test/base/test_launcher_utils.h"
30 31 32 33 34 35

namespace {

// This is for the code that is to be ran in multiple threads at once,
// to stress a race condition on first process start.
// We use the thread safe ref counted base class so that we can use the
36
// base::Bind to run the StartChrome methods in many threads.
37 38
class ChromeStarter : public base::RefCountedThreadSafe<ChromeStarter> {
 public:
39
  ChromeStarter(base::TimeDelta timeout, const base::FilePath& user_data_dir)
40 41
      : ready_event_(false /* manual */, false /* signaled */),
        done_event_(false /* manual */, false /* signaled */),
42
        process_handle_(base::kNullProcessHandle),
43
        process_terminated_(false),
44
        timeout_(timeout),
45
        user_data_dir_(user_data_dir) {
46 47 48 49 50 51 52
  }

  // We must reset some data members since we reuse the same ChromeStarter
  // object and start/stop it a few times. We must start fresh! :-)
  void Reset() {
    ready_event_.Reset();
    done_event_.Reset();
53
    if (process_handle_ != base::kNullProcessHandle)
54
      base::CloseProcessHandle(process_handle_);
55
    process_handle_ = base::kNullProcessHandle;
56 57 58
    process_terminated_ = false;
  }

59 60 61
  void StartChrome(base::WaitableEvent* start_event, bool first_run) {
    // TODO(mattm): maybe stuff should be refactored to use
    // UITest::LaunchBrowserHelper somehow?
62
    base::FilePath program;
63
    ASSERT_TRUE(PathService::Get(base::FILE_EXE, &program));
64
    base::CommandLine command_line(program);
65
    command_line.AppendSwitchPath(switches::kUserDataDir, user_data_dir_);
66 67

    if (first_run)
68
      command_line.AppendSwitch(switches::kForceFirstRun);
69 70
    else
      command_line.AppendSwitch(switches::kNoFirstRun);
71

72 73
    // Add the normal test-mode switches, except for the ones we're adding
    // ourselves.
74
    base::CommandLine standard_switches(base::CommandLine::NO_PROGRAM);
75
    test_launcher_utils::PrepareBrowserCommandLineForTests(&standard_switches);
76 77 78
    const base::CommandLine::SwitchMap& switch_map =
        standard_switches.GetSwitches();
    for (base::CommandLine::SwitchMap::const_iterator i = switch_map.begin();
79 80 81
         i != switch_map.end(); ++i) {
      const std::string& switch_name = i->first;
      if (switch_name == switches::kUserDataDir ||
82
          switch_name == switches::kForceFirstRun ||
83 84 85 86 87 88
          switch_name == switches::kNoFirstRun)
        continue;

      command_line.AppendSwitchNative(switch_name, i->second);
    }

89 90 91 92 93
    // Try to get all threads to launch the app at the same time.
    // So let the test know we are ready.
    ready_event_.Signal();
    // And then wait for the test to tell us to GO!
    ASSERT_NE(static_cast<base::WaitableEvent*>(NULL), start_event);
94
    start_event->Wait();
95 96 97 98

    // Here we don't wait for the app to be terminated because one of the
    // process will stay alive while the others will be restarted. If we would
    // wait here, we would never get a handle to the main process...
99
    base::LaunchProcess(command_line, base::LaunchOptions(), &process_handle_);
100
    ASSERT_NE(base::kNullProcessHandle, process_handle_);
101 102 103 104 105

    // We can wait on the handle here, we should get stuck on one and only
    // one process. The test below will take care of killing that process
    // to unstuck us once it confirms there is only one.
    process_terminated_ = base::WaitForSingleProcess(process_handle_,
106
                                                     timeout_);
107 108 109 110 111 112 113 114 115 116 117 118
    // Let the test know we are done.
    done_event_.Signal();
  }

  // Public access to simplify the test code using them.
  base::WaitableEvent ready_event_;
  base::WaitableEvent done_event_;
  base::ProcessHandle process_handle_;
  bool process_terminated_;

 private:
  friend class base::RefCountedThreadSafe<ChromeStarter>;
119

120
  ~ChromeStarter() {
121
    if (process_handle_ != base::kNullProcessHandle)
122 123
      base::CloseProcessHandle(process_handle_);
  }
124

125
  base::TimeDelta timeout_;
126
  base::FilePath user_data_dir_;
127

128 129 130
  DISALLOW_COPY_AND_ASSIGN(ChromeStarter);
};

131 132
}  // namespace

133
// Our test fixture that initializes and holds onto a few global vars.
134
class ProcessSingletonTest : public InProcessBrowserTest {
135
 public:
136
  ProcessSingletonTest()
137 138 139
      // We use a manual reset so that all threads wake up at once when signaled
      // and thus we must manually reset it for each attempt.
      : threads_waker_(true /* manual */, false /* signaled */) {
140
    EXPECT_TRUE(temp_profile_dir_.CreateUniqueTempDir());
141 142
  }

143
  void SetUp() override {
144 145 146 147
    // Start the threads and create the starters.
    for (size_t i = 0; i < kNbThreads; ++i) {
      chrome_starter_threads_[i].reset(new base::Thread("ChromeStarter"));
      ASSERT_TRUE(chrome_starter_threads_[i]->Start());
148
      chrome_starters_[i] = new ChromeStarter(
149
          TestTimeouts::action_max_timeout(), temp_profile_dir_.path());
150 151 152
    }
  }

153
  void TearDown() override {
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
    // Stop the threads.
    for (size_t i = 0; i < kNbThreads; ++i)
      chrome_starter_threads_[i]->Stop();
  }

  // This method is used to make sure we kill the main browser process after
  // all of its child processes have successfully attached to it. This was added
  // when we realized that if we just kill the parent process right away, we
  // sometimes end up with dangling child processes. If we Sleep for a certain
  // amount of time, we are OK... So we introduced this method to avoid a
  // flaky wait. Instead, we kill all descendants of the main process after we
  // killed it, relying on the fact that we can still get the parent id of a
  // child process, even when the parent dies.
  void KillProcessTree(base::ProcessHandle process_handle) {
    class ProcessTreeFilter : public base::ProcessFilter {
     public:
      explicit ProcessTreeFilter(base::ProcessId parent_pid) {
        ancestor_pids_.insert(parent_pid);
      }
173
      bool Includes(const base::ProcessEntry& entry) const override {
174 175
        if (ancestor_pids_.find(entry.parent_pid()) != ancestor_pids_.end()) {
          ancestor_pids_.insert(entry.pid());
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
          return true;
        } else {
          return false;
        }
      }
     private:
      mutable std::set<base::ProcessId> ancestor_pids_;
    } process_tree_filter(base::GetProcId(process_handle));

    // Start by explicitly killing the main process we know about...
    static const int kExitCode = 42;
    EXPECT_TRUE(base::KillProcess(process_handle, kExitCode, true /* wait */));

    // Then loop until we can't find any of its descendant.
    // But don't try more than kNbTries times...
    static const int kNbTries = 10;
    int num_tries = 0;
193
    base::FilePath program;
194
    ASSERT_TRUE(PathService::Get(base::FILE_EXE, &program));
195
    base::FilePath::StringType exe_name = program.BaseName().value();
196 197 198
    while (base::GetProcessCount(exe_name, &process_tree_filter) > 0 &&
           num_tries++ < kNbTries) {
      base::KillProcesses(exe_name, kExitCode, &process_tree_filter);
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
    }
    DLOG_IF(ERROR, num_tries >= kNbTries) << "Failed to kill all processes!";
  }

  // Since this is a hard to reproduce problem, we make a few attempts.
  // We stop the attempts at the first error, and when there are no errors,
  // we don't time-out of any wait, so it executes quite fast anyway.
  static const size_t kNbAttempts = 5;

  // The idea is to start chrome from multiple threads all at once.
  static const size_t kNbThreads = 5;
  scoped_refptr<ChromeStarter> chrome_starters_[kNbThreads];
  scoped_ptr<base::Thread> chrome_starter_threads_[kNbThreads];

  // The event that will get all threads to wake up simultaneously and try
  // to start a chrome process at the same time.
  base::WaitableEvent threads_waker_;
216 217 218

  // We don't want to use the default profile, but can't use UITest's since we
  // don't use UITest::LaunchBrowser.
219
  base::ScopedTempDir temp_profile_dir_;
220 221
};

222 223
#if defined(OS_LINUX) && defined(TOOLKIT_VIEWS)
// http://crbug.com/58219
224
#define MAYBE_StartupRaceCondition DISABLED_StartupRaceCondition
225 226 227
#else
#define MAYBE_StartupRaceCondition StartupRaceCondition
#endif
228
IN_PROC_BROWSER_TEST_F(ProcessSingletonTest, MAYBE_StartupRaceCondition) {
229 230 231 232 233 234 235 236
  // We use this to stop the attempts loop on the first failure.
  bool failed = false;
  for (size_t attempt = 0; attempt < kNbAttempts && !failed; ++attempt) {
    SCOPED_TRACE(testing::Message() << "Attempt: " << attempt << ".");
    // We use a single event to get all threads to do the AppLaunch at the same
    // time...
    threads_waker_.Reset();

237 238 239 240 241 242 243 244 245 246 247 248 249 250
    // Test both with and without the first-run dialog, since they exercise
    // different paths.
#if defined(OS_POSIX)
    // TODO(mattm): test first run dialog singleton handling on linux too.
    // On posix if we test the first run dialog, GracefulShutdownHandler gets
    // the TERM signal, but since the message loop isn't running during the gtk
    // first run dialog, the ShutdownDetector never handles it, and KillProcess
    // has to time out (60 sec!) and SIGKILL.
    bool first_run = false;
#else
    // Test for races in both regular start up and first run start up cases.
    bool first_run = attempt % 2;
#endif

251 252 253 254 255 256 257
    // Here we prime all the threads with a ChromeStarter that will wait for
    // our signal to launch its chrome process.
    for (size_t i = 0; i < kNbThreads; ++i) {
      ASSERT_NE(static_cast<ChromeStarter*>(NULL), chrome_starters_[i].get());
      chrome_starters_[i]->Reset();

      ASSERT_TRUE(chrome_starter_threads_[i]->IsRunning());
258
      ASSERT_NE(static_cast<base::MessageLoop*>(NULL),
259 260 261
                chrome_starter_threads_[i]->message_loop());

      chrome_starter_threads_[i]->message_loop()->PostTask(
262 263 264 265
          FROM_HERE, base::Bind(&ChromeStarter::StartChrome,
                                chrome_starters_[i].get(),
                                &threads_waker_,
                                first_run));
266 267 268 269 270 271
    }

    // Wait for all the starters to be ready.
    // We could replace this loop if we ever implement a WaitAll().
    for (size_t i = 0; i < kNbThreads; ++i) {
      SCOPED_TRACE(testing::Message() << "Waiting on thread: " << i << ".");
272
      chrome_starters_[i]->ready_event_.Wait();
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
    }
    // GO!
    threads_waker_.Signal();

    // As we wait for all threads to signal that they are done, we remove their
    // index from this vector so that we get left with only the index of
    // the thread that started the main process.
    std::vector<size_t> pending_starters(kNbThreads);
    for (size_t i = 0; i < kNbThreads; ++i)
      pending_starters[i] = i;

    // We use a local array of starter's done events we must wait on...
    // These are collected from the starters that we have not yet been removed
    // from the pending_starters vector.
    base::WaitableEvent* starters_done_events[kNbThreads];
    // At the end, "There can be only one" main browser process alive.
    while (pending_starters.size() > 1) {
      SCOPED_TRACE(testing::Message() << pending_starters.size() <<
                   " starters left.");
      for (size_t i = 0; i < pending_starters.size(); ++i) {
        starters_done_events[i] =
            &chrome_starters_[pending_starters[i]]->done_event_;
      }
      size_t done_index = base::WaitableEvent::WaitMany(
          starters_done_events, pending_starters.size());
      size_t starter_index = pending_starters[done_index];
      // If the starter is done but has not marked itself as terminated,
      // it is because it timed out of its WaitForSingleProcess(). Only the
      // last one standing should be left waiting... So we failed...
      EXPECT_TRUE(chrome_starters_[starter_index]->process_terminated_ ||
                  failed) << "There is more than one main process.";
      if (!chrome_starters_[starter_index]->process_terminated_) {
        // This will stop the "for kNbAttempts" loop.
        failed = true;
        // But we let the last loop turn finish so that we can properly
        // kill all remaining processes. Starting with this one...
309 310
        if (chrome_starters_[starter_index]->process_handle_ !=
            base::kNullProcessHandle) {
311 312 313 314 315 316 317 318 319
          KillProcessTree(chrome_starters_[starter_index]->process_handle_);
        }
      }
      pending_starters.erase(pending_starters.begin() + done_index);
    }

    // "There can be only one!" :-)
    ASSERT_EQ(static_cast<size_t>(1), pending_starters.size());
    size_t last_index = pending_starters.front();
320
    pending_starters.clear();
321 322
    if (chrome_starters_[last_index]->process_handle_ !=
        base::kNullProcessHandle) {
323 324 325 326 327
      KillProcessTree(chrome_starters_[last_index]->process_handle_);
      chrome_starters_[last_index]->done_event_.Wait();
    }
  }
}