| // Copyright (c) 2012 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 test validates that the ProcessSingleton class properly makes sure | 
 | // that there is only one main browser process. | 
 | // | 
 | // 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?) | 
 |  | 
 | #include <stddef.h> | 
 |  | 
 | #include <memory> | 
 |  | 
 | #include "base/bind.h" | 
 | #include "base/command_line.h" | 
 | #include "base/files/file_path.h" | 
 | #include "base/files/scoped_temp_dir.h" | 
 | #include "base/location.h" | 
 | #include "base/memory/ref_counted.h" | 
 | #include "base/path_service.h" | 
 | #include "base/process/launch.h" | 
 | #include "base/process/process.h" | 
 | #include "base/process/process_iterator.h" | 
 | #include "base/synchronization/waitable_event.h" | 
 | #include "base/task/single_thread_task_runner.h" | 
 | #include "base/test/test_timeouts.h" | 
 | #include "base/threading/thread.h" | 
 | #include "base/time/time.h" | 
 | #include "build/build_config.h" | 
 | #include "chrome/common/chrome_constants.h" | 
 | #include "chrome/common/chrome_paths.h" | 
 | #include "chrome/common/chrome_result_codes.h" | 
 | #include "chrome/common/chrome_switches.h" | 
 | #include "chrome/test/base/in_process_browser_test.h" | 
 | #include "chrome/test/base/test_launcher_utils.h" | 
 | #include "content/public/test/browser_test.h" | 
 | #include "testing/gmock/include/gmock/gmock.h" | 
 |  | 
 | using ::testing::AnyOf; | 
 | using ::testing::Eq; | 
 |  | 
 | 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 | 
 | // base::Bind to run the StartChrome methods in many threads. | 
 | class ChromeStarter : public base::RefCountedThreadSafe<ChromeStarter> { | 
 |  public: | 
 |   ChromeStarter(base::TimeDelta timeout, | 
 |                 const base::FilePath& user_data_dir, | 
 |                 const base::CommandLine& initial_command_line_for_relaunch) | 
 |       : ready_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC, | 
 |                      base::WaitableEvent::InitialState::NOT_SIGNALED), | 
 |         done_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC, | 
 |                     base::WaitableEvent::InitialState::NOT_SIGNALED), | 
 |         process_terminated_(false), | 
 |         timeout_(timeout), | 
 |         user_data_dir_(user_data_dir), | 
 |         initial_command_line_for_relaunch_(initial_command_line_for_relaunch) {} | 
 |  | 
 |   ChromeStarter(const ChromeStarter&) = delete; | 
 |   ChromeStarter& operator=(const ChromeStarter&) = delete; | 
 |  | 
 |   // 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(); | 
 |     if (process_.IsValid()) | 
 |       process_.Close(); | 
 |     process_terminated_ = false; | 
 |   } | 
 |  | 
 |   void StartChrome(base::WaitableEvent* start_event, bool first_run) { | 
 |     base::CommandLine command_line_for_relaunch( | 
 |         initial_command_line_for_relaunch_.GetProgram()); | 
 |     test_launcher_utils::RemoveCommandLineSwitch( | 
 |         initial_command_line_for_relaunch_, switches::kUserDataDir, | 
 |         &command_line_for_relaunch); | 
 |     command_line_for_relaunch.AppendSwitchPath(switches::kUserDataDir, | 
 |                                                user_data_dir_); | 
 |  | 
 |     if (first_run) { | 
 |       base::CommandLine tmp_command_line = command_line_for_relaunch; | 
 |       test_launcher_utils::RemoveCommandLineSwitch( | 
 |           tmp_command_line, switches::kNoFirstRun, &command_line_for_relaunch); | 
 |       command_line_for_relaunch.AppendSwitch(switches::kForceFirstRun); | 
 |     } | 
 |  | 
 |     // 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(nullptr, start_event); | 
 |     start_event->Wait(); | 
 |  | 
 |     // 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... | 
 |     process_ = | 
 |         base::LaunchProcess(command_line_for_relaunch, base::LaunchOptions()); | 
 |     ASSERT_TRUE(process_.IsValid()); | 
 |  | 
 |     // 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_ = | 
 |         process_.WaitForExitWithTimeout(timeout_, &exit_code_); | 
 |     // 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::Process process_; | 
 |   bool process_terminated_; | 
 |   // Process exit code. Only meaningful if |process_terminated_| is true. | 
 |   int exit_code_; | 
 |  | 
 |  private: | 
 |   friend class base::RefCountedThreadSafe<ChromeStarter>; | 
 |  | 
 |   ~ChromeStarter() {} | 
 |  | 
 |   base::TimeDelta timeout_; | 
 |   base::FilePath user_data_dir_; | 
 |   base::CommandLine initial_command_line_for_relaunch_; | 
 | }; | 
 |  | 
 | }  // namespace | 
 |  | 
 | // Our test fixture that initializes and holds onto a few global vars. | 
 | class ProcessSingletonTest : public InProcessBrowserTest { | 
 |  public: | 
 |   ProcessSingletonTest() | 
 |       // 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_(base::WaitableEvent::ResetPolicy::MANUAL, | 
 |                        base::WaitableEvent::InitialState::NOT_SIGNALED) { | 
 |     EXPECT_TRUE(temp_profile_dir_.CreateUniqueTempDir()); | 
 |   } | 
 |  | 
 |   void TearDown() override { | 
 |     InProcessBrowserTest::TearDown(); | 
 |     // 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(const base::Process& process) { | 
 |     class ProcessTreeFilter : public base::ProcessFilter { | 
 |      public: | 
 |       explicit ProcessTreeFilter(base::ProcessId parent_pid) { | 
 |         ancestor_pids_.insert(parent_pid); | 
 |       } | 
 |       bool Includes(const base::ProcessEntry& entry) const override { | 
 |         if (ancestor_pids_.find(entry.parent_pid()) != ancestor_pids_.end()) { | 
 |           ancestor_pids_.insert(entry.pid()); | 
 |           return true; | 
 |         } else { | 
 |           return false; | 
 |         } | 
 |       } | 
 |      private: | 
 |       mutable std::set<base::ProcessId> ancestor_pids_; | 
 |     } process_tree_filter(process.Pid()); | 
 |  | 
 |     // Start by explicitly killing the main process we know about... | 
 |     static const int kExitCode = 42; | 
 |     EXPECT_TRUE(process.Terminate(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; | 
 |     base::FilePath program; | 
 |     ASSERT_TRUE(base::PathService::Get(base::FILE_EXE, &program)); | 
 |     base::FilePath::StringType exe_name = program.BaseName().value(); | 
 |     while (base::GetProcessCount(exe_name, &process_tree_filter) > 0 && | 
 |            num_tries++ < kNbTries) { | 
 |       base::KillProcesses(exe_name, kExitCode, &process_tree_filter); | 
 |     } | 
 |     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]; | 
 |   std::unique_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_; | 
 |  | 
 |   // We don't want to use the default profile, but can't use UITest's since we | 
 |   // don't use UITest::LaunchBrowser. | 
 |   base::ScopedTempDir temp_profile_dir_; | 
 | }; | 
 |  | 
 | // ChromeOS hits DCHECKS on ProcessSingleton rendezvous: crbug.com/782487 | 
 | #if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) | 
 | #define MAYBE_StartupRaceCondition DISABLED_StartupRaceCondition | 
 | #else | 
 | #define MAYBE_StartupRaceCondition StartupRaceCondition | 
 | #endif | 
 | IN_PROC_BROWSER_TEST_F(ProcessSingletonTest, MAYBE_StartupRaceCondition) { | 
 |   // Start the threads and create the starters. | 
 |   for (size_t i = 0; i < kNbThreads; ++i) { | 
 |     chrome_starter_threads_[i] = | 
 |         std::make_unique<base::Thread>("ChromeStarter"); | 
 |     ASSERT_TRUE(chrome_starter_threads_[i]->Start()); | 
 |     chrome_starters_[i] = base::MakeRefCounted<ChromeStarter>( | 
 |         TestTimeouts::action_max_timeout(), temp_profile_dir_.GetPath(), | 
 |         GetCommandLineForRelaunch()); | 
 |   } | 
 |  | 
 |   for (size_t attempt = 0; attempt < kNbAttempts && !HasFailure(); ++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(); | 
 |  | 
 |     // Test both with and without the first-run dialog, since they exercise | 
 |     // different paths. | 
 | #if BUILDFLAG(IS_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 | 
 |  | 
 |     // 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()); | 
 |       ASSERT_TRUE(chrome_starter_threads_[i]->task_runner()); | 
 |  | 
 |       chrome_starter_threads_[i]->task_runner()->PostTask( | 
 |           FROM_HERE, | 
 |           base::BindOnce(&ChromeStarter::StartChrome, chrome_starters_[i], | 
 |                          &threads_waker_, first_run)); | 
 |     } | 
 |  | 
 |     // 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 << "."); | 
 |       chrome_starters_[i]->ready_event_.Wait(); | 
 |     } | 
 |     // 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 WaitForExitCodeWithTimeout(). Only | 
 |       // the last one standing should be left waiting... So we failed... | 
 |       EXPECT_TRUE(chrome_starters_[starter_index]->process_terminated_) | 
 |           << "There is more than one main process."; | 
 |       if (chrome_starters_[starter_index]->process_terminated_) { | 
 |         // Generally PROCESS_NOTIFIED would be the expected exit code. In some | 
 |         // rare cases the ProcessSingleton race can result in PROFILE_IN_USE | 
 |         // exit code, which we also allow, though it would be ideal if that | 
 |         // never happened. | 
 |         // TODO(mattm): investigate why PROFILE_IN_USE occurs sometimes. | 
 |         EXPECT_THAT( | 
 |             chrome_starters_[starter_index]->exit_code_, | 
 |             AnyOf(Eq(chrome::RESULT_CODE_PROFILE_IN_USE), | 
 |                   Eq(chrome::RESULT_CODE_NORMAL_EXIT_PROCESS_NOTIFIED))); | 
 |       } else { | 
 |         // But we let the last loop turn finish so that we can properly | 
 |         // kill all remaining processes. Starting with this one... | 
 |         if (chrome_starters_[starter_index]->process_.IsValid()) { | 
 |           KillProcessTree(chrome_starters_[starter_index]->process_); | 
 |         } | 
 |       } | 
 |       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(); | 
 |     pending_starters.clear(); | 
 |     if (chrome_starters_[last_index]->process_.IsValid()) { | 
 |       KillProcessTree(chrome_starters_[last_index]->process_); | 
 |       chrome_starters_[last_index]->done_event_.Wait(); | 
 |     } | 
 |   } | 
 | } |