| OLD | NEW | 
 | (Empty) | 
|    1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |  | 
|    2 // Use of this source code is governed by a BSD-style license that can be |  | 
|    3 // found in the LICENSE file. |  | 
|    4  |  | 
|    5 // This test validates that the ProcessSingleton class properly makes sure |  | 
|    6 // that there is only one main browser process. |  | 
|    7 // |  | 
|    8 // It is currently compiled and run on Windows and Posix(non-Mac) platforms. |  | 
|    9 // Mac uses system services and ProcessSingletonMac is a noop.  (Maybe it still |  | 
|   10 // makes sense to test that the system services are giving the behavior we |  | 
|   11 // want?) |  | 
|   12  |  | 
|   13 #include <list> |  | 
|   14  |  | 
|   15 #include "base/bind.h" |  | 
|   16 #include "base/file_path.h" |  | 
|   17 #include "base/file_util.h" |  | 
|   18 #include "base/memory/ref_counted.h" |  | 
|   19 #include "base/path_service.h" |  | 
|   20 #include "base/process_util.h" |  | 
|   21 #include "base/scoped_temp_dir.h" |  | 
|   22 #include "base/synchronization/waitable_event.h" |  | 
|   23 #include "base/test/test_timeouts.h" |  | 
|   24 #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/test_launcher_utils.h" |  | 
|   29 #include "chrome/test/ui/ui_test.h" |  | 
|   30 #include "testing/gtest/include/gtest/gtest.h" |  | 
|   31  |  | 
|   32 namespace { |  | 
|   33  |  | 
|   34 // This is for the code that is to be ran in multiple threads at once, |  | 
|   35 // to stress a race condition on first process start. |  | 
|   36 // We use the thread safe ref counted base class so that we can use the |  | 
|   37 // base::Bind to run the StartChrome methods in many threads. |  | 
|   38 class ChromeStarter : public base::RefCountedThreadSafe<ChromeStarter> { |  | 
|   39  public: |  | 
|   40   ChromeStarter(int timeout_ms, const FilePath& user_data_dir) |  | 
|   41       : ready_event_(false /* manual */, false /* signaled */), |  | 
|   42         done_event_(false /* manual */, false /* signaled */), |  | 
|   43         process_handle_(base::kNullProcessHandle), |  | 
|   44         process_terminated_(false), |  | 
|   45         timeout_ms_(timeout_ms), |  | 
|   46         user_data_dir_(user_data_dir) { |  | 
|   47   } |  | 
|   48  |  | 
|   49   // We must reset some data members since we reuse the same ChromeStarter |  | 
|   50   // object and start/stop it a few times. We must start fresh! :-) |  | 
|   51   void Reset() { |  | 
|   52     ready_event_.Reset(); |  | 
|   53     done_event_.Reset(); |  | 
|   54     if (process_handle_ != base::kNullProcessHandle) |  | 
|   55       base::CloseProcessHandle(process_handle_); |  | 
|   56     process_handle_ = base::kNullProcessHandle; |  | 
|   57     process_terminated_ = false; |  | 
|   58   } |  | 
|   59  |  | 
|   60   void StartChrome(base::WaitableEvent* start_event, bool first_run) { |  | 
|   61     // TODO(mattm): maybe stuff should be refactored to use |  | 
|   62     // UITest::LaunchBrowserHelper somehow? |  | 
|   63     FilePath browser_directory; |  | 
|   64     PathService::Get(chrome::DIR_APP, &browser_directory); |  | 
|   65     CommandLine command_line(browser_directory.Append( |  | 
|   66         chrome::kBrowserProcessExecutablePath)); |  | 
|   67  |  | 
|   68     command_line.AppendSwitchPath(switches::kUserDataDir, user_data_dir_); |  | 
|   69  |  | 
|   70     if (first_run) |  | 
|   71       command_line.AppendSwitch(switches::kFirstRun); |  | 
|   72     else |  | 
|   73       command_line.AppendSwitch(switches::kNoFirstRun); |  | 
|   74  |  | 
|   75     // Add the normal test-mode switches, except for the ones we're adding |  | 
|   76     // ourselves. |  | 
|   77     CommandLine standard_switches(CommandLine::NO_PROGRAM); |  | 
|   78     test_launcher_utils::PrepareBrowserCommandLineForTests(&standard_switches); |  | 
|   79     const CommandLine::SwitchMap& switch_map = standard_switches.GetSwitches(); |  | 
|   80     for (CommandLine::SwitchMap::const_iterator i = switch_map.begin(); |  | 
|   81          i != switch_map.end(); ++i) { |  | 
|   82       const std::string& switch_name = i->first; |  | 
|   83       if (switch_name == switches::kUserDataDir || |  | 
|   84           switch_name == switches::kFirstRun || |  | 
|   85           switch_name == switches::kNoFirstRun) |  | 
|   86         continue; |  | 
|   87  |  | 
|   88       command_line.AppendSwitchNative(switch_name, i->second); |  | 
|   89     } |  | 
|   90  |  | 
|   91     // Try to get all threads to launch the app at the same time. |  | 
|   92     // So let the test know we are ready. |  | 
|   93     ready_event_.Signal(); |  | 
|   94     // And then wait for the test to tell us to GO! |  | 
|   95     ASSERT_NE(static_cast<base::WaitableEvent*>(NULL), start_event); |  | 
|   96     start_event->Wait(); |  | 
|   97  |  | 
|   98     // Here we don't wait for the app to be terminated because one of the |  | 
|   99     // process will stay alive while the others will be restarted. If we would |  | 
|  100     // wait here, we would never get a handle to the main process... |  | 
|  101     base::LaunchProcess(command_line, base::LaunchOptions(), &process_handle_); |  | 
|  102     ASSERT_NE(base::kNullProcessHandle, process_handle_); |  | 
|  103  |  | 
|  104     // We can wait on the handle here, we should get stuck on one and only |  | 
|  105     // one process. The test below will take care of killing that process |  | 
|  106     // to unstuck us once it confirms there is only one. |  | 
|  107     process_terminated_ = base::WaitForSingleProcess(process_handle_, |  | 
|  108                                                      timeout_ms_); |  | 
|  109     // Let the test know we are done. |  | 
|  110     done_event_.Signal(); |  | 
|  111   } |  | 
|  112  |  | 
|  113   // Public access to simplify the test code using them. |  | 
|  114   base::WaitableEvent ready_event_; |  | 
|  115   base::WaitableEvent done_event_; |  | 
|  116   base::ProcessHandle process_handle_; |  | 
|  117   bool process_terminated_; |  | 
|  118  |  | 
|  119  private: |  | 
|  120   friend class base::RefCountedThreadSafe<ChromeStarter>; |  | 
|  121  |  | 
|  122   ~ChromeStarter() { |  | 
|  123     if (process_handle_ != base::kNullProcessHandle) |  | 
|  124       base::CloseProcessHandle(process_handle_); |  | 
|  125   } |  | 
|  126  |  | 
|  127   int timeout_ms_; |  | 
|  128   FilePath user_data_dir_; |  | 
|  129  |  | 
|  130   DISALLOW_COPY_AND_ASSIGN(ChromeStarter); |  | 
|  131 }; |  | 
|  132  |  | 
|  133 // Our test fixture that initializes and holds onto a few global vars. |  | 
|  134 class ProcessSingletonTest : public UITest { |  | 
|  135  public: |  | 
|  136   ProcessSingletonTest() |  | 
|  137       // We use a manual reset so that all threads wake up at once when signaled |  | 
|  138       // and thus we must manually reset it for each attempt. |  | 
|  139       : threads_waker_(true /* manual */, false /* signaled */) { |  | 
|  140     EXPECT_TRUE(temp_profile_dir_.CreateUniqueTempDir()); |  | 
|  141   } |  | 
|  142  |  | 
|  143   void SetUp() { |  | 
|  144     // Start the threads and create the starters. |  | 
|  145     for (size_t i = 0; i < kNbThreads; ++i) { |  | 
|  146       chrome_starter_threads_[i].reset(new base::Thread("ChromeStarter")); |  | 
|  147       ASSERT_TRUE(chrome_starter_threads_[i]->Start()); |  | 
|  148       chrome_starters_[i] = new ChromeStarter( |  | 
|  149           TestTimeouts::action_max_timeout_ms(), temp_profile_dir_.path()); |  | 
|  150     } |  | 
|  151   } |  | 
|  152  |  | 
|  153   void TearDown() { |  | 
|  154     // Stop the threads. |  | 
|  155     for (size_t i = 0; i < kNbThreads; ++i) |  | 
|  156       chrome_starter_threads_[i]->Stop(); |  | 
|  157   } |  | 
|  158  |  | 
|  159   // This method is used to make sure we kill the main browser process after |  | 
|  160   // all of its child processes have successfully attached to it. This was added |  | 
|  161   // when we realized that if we just kill the parent process right away, we |  | 
|  162   // sometimes end up with dangling child processes. If we Sleep for a certain |  | 
|  163   // amount of time, we are OK... So we introduced this method to avoid a |  | 
|  164   // flaky wait. Instead, we kill all descendants of the main process after we |  | 
|  165   // killed it, relying on the fact that we can still get the parent id of a |  | 
|  166   // child process, even when the parent dies. |  | 
|  167   void KillProcessTree(base::ProcessHandle process_handle) { |  | 
|  168     class ProcessTreeFilter : public base::ProcessFilter { |  | 
|  169      public: |  | 
|  170       explicit ProcessTreeFilter(base::ProcessId parent_pid) { |  | 
|  171         ancestor_pids_.insert(parent_pid); |  | 
|  172       } |  | 
|  173       virtual bool Includes(const base::ProcessEntry & entry) const { |  | 
|  174         if (ancestor_pids_.find(entry.parent_pid()) != ancestor_pids_.end()) { |  | 
|  175           ancestor_pids_.insert(entry.pid()); |  | 
|  176           return true; |  | 
|  177         } else { |  | 
|  178           return false; |  | 
|  179         } |  | 
|  180       } |  | 
|  181      private: |  | 
|  182       mutable std::set<base::ProcessId> ancestor_pids_; |  | 
|  183     } process_tree_filter(base::GetProcId(process_handle)); |  | 
|  184  |  | 
|  185     // Start by explicitly killing the main process we know about... |  | 
|  186     static const int kExitCode = 42; |  | 
|  187     EXPECT_TRUE(base::KillProcess(process_handle, kExitCode, true /* wait */)); |  | 
|  188  |  | 
|  189     // Then loop until we can't find any of its descendant. |  | 
|  190     // But don't try more than kNbTries times... |  | 
|  191     static const int kNbTries = 10; |  | 
|  192     int num_tries = 0; |  | 
|  193     while (base::GetProcessCount(chrome::kBrowserProcessExecutablePath, |  | 
|  194         &process_tree_filter) > 0 && num_tries++ < kNbTries) { |  | 
|  195       base::KillProcesses(chrome::kBrowserProcessExecutablePath, |  | 
|  196                           kExitCode, &process_tree_filter); |  | 
|  197     } |  | 
|  198     DLOG_IF(ERROR, num_tries >= kNbTries) << "Failed to kill all processes!"; |  | 
|  199   } |  | 
|  200  |  | 
|  201   // Since this is a hard to reproduce problem, we make a few attempts. |  | 
|  202   // We stop the attempts at the first error, and when there are no errors, |  | 
|  203   // we don't time-out of any wait, so it executes quite fast anyway. |  | 
|  204   static const size_t kNbAttempts = 5; |  | 
|  205  |  | 
|  206   // The idea is to start chrome from multiple threads all at once. |  | 
|  207   static const size_t kNbThreads = 5; |  | 
|  208   scoped_refptr<ChromeStarter> chrome_starters_[kNbThreads]; |  | 
|  209   scoped_ptr<base::Thread> chrome_starter_threads_[kNbThreads]; |  | 
|  210  |  | 
|  211   // The event that will get all threads to wake up simultaneously and try |  | 
|  212   // to start a chrome process at the same time. |  | 
|  213   base::WaitableEvent threads_waker_; |  | 
|  214  |  | 
|  215   // We don't want to use the default profile, but can't use UITest's since we |  | 
|  216   // don't use UITest::LaunchBrowser. |  | 
|  217   ScopedTempDir temp_profile_dir_; |  | 
|  218 }; |  | 
|  219  |  | 
|  220 #if defined(OS_LINUX) && defined(TOOLKIT_VIEWS) |  | 
|  221 // http://crbug.com/58219 |  | 
|  222 #define MAYBE_StartupRaceCondition FAILS_StartupRaceCondition |  | 
|  223 #else |  | 
|  224 #define MAYBE_StartupRaceCondition StartupRaceCondition |  | 
|  225 #endif |  | 
|  226 TEST_F(ProcessSingletonTest, MAYBE_StartupRaceCondition) { |  | 
|  227   // We use this to stop the attempts loop on the first failure. |  | 
|  228   bool failed = false; |  | 
|  229   for (size_t attempt = 0; attempt < kNbAttempts && !failed; ++attempt) { |  | 
|  230     SCOPED_TRACE(testing::Message() << "Attempt: " << attempt << "."); |  | 
|  231     // We use a single event to get all threads to do the AppLaunch at the same |  | 
|  232     // time... |  | 
|  233     threads_waker_.Reset(); |  | 
|  234  |  | 
|  235     // Test both with and without the first-run dialog, since they exercise |  | 
|  236     // different paths. |  | 
|  237 #if defined(OS_POSIX) |  | 
|  238     // TODO(mattm): test first run dialog singleton handling on linux too. |  | 
|  239     // On posix if we test the first run dialog, GracefulShutdownHandler gets |  | 
|  240     // the TERM signal, but since the message loop isn't running during the gtk |  | 
|  241     // first run dialog, the ShutdownDetector never handles it, and KillProcess |  | 
|  242     // has to time out (60 sec!) and SIGKILL. |  | 
|  243     bool first_run = false; |  | 
|  244 #else |  | 
|  245     // Test for races in both regular start up and first run start up cases. |  | 
|  246     bool first_run = attempt % 2; |  | 
|  247 #endif |  | 
|  248  |  | 
|  249     // Here we prime all the threads with a ChromeStarter that will wait for |  | 
|  250     // our signal to launch its chrome process. |  | 
|  251     for (size_t i = 0; i < kNbThreads; ++i) { |  | 
|  252       ASSERT_NE(static_cast<ChromeStarter*>(NULL), chrome_starters_[i].get()); |  | 
|  253       chrome_starters_[i]->Reset(); |  | 
|  254  |  | 
|  255       ASSERT_TRUE(chrome_starter_threads_[i]->IsRunning()); |  | 
|  256       ASSERT_NE(static_cast<MessageLoop*>(NULL), |  | 
|  257                 chrome_starter_threads_[i]->message_loop()); |  | 
|  258  |  | 
|  259       chrome_starter_threads_[i]->message_loop()->PostTask( |  | 
|  260           FROM_HERE, base::Bind(&ChromeStarter::StartChrome, |  | 
|  261                                 chrome_starters_[i].get(), |  | 
|  262                                 &threads_waker_, |  | 
|  263                                 first_run)); |  | 
|  264     } |  | 
|  265  |  | 
|  266     // Wait for all the starters to be ready. |  | 
|  267     // We could replace this loop if we ever implement a WaitAll(). |  | 
|  268     for (size_t i = 0; i < kNbThreads; ++i) { |  | 
|  269       SCOPED_TRACE(testing::Message() << "Waiting on thread: " << i << "."); |  | 
|  270       chrome_starters_[i]->ready_event_.Wait(); |  | 
|  271     } |  | 
|  272     // GO! |  | 
|  273     threads_waker_.Signal(); |  | 
|  274  |  | 
|  275     // As we wait for all threads to signal that they are done, we remove their |  | 
|  276     // index from this vector so that we get left with only the index of |  | 
|  277     // the thread that started the main process. |  | 
|  278     std::vector<size_t> pending_starters(kNbThreads); |  | 
|  279     for (size_t i = 0; i < kNbThreads; ++i) |  | 
|  280       pending_starters[i] = i; |  | 
|  281  |  | 
|  282     // We use a local array of starter's done events we must wait on... |  | 
|  283     // These are collected from the starters that we have not yet been removed |  | 
|  284     // from the pending_starters vector. |  | 
|  285     base::WaitableEvent* starters_done_events[kNbThreads]; |  | 
|  286     // At the end, "There can be only one" main browser process alive. |  | 
|  287     while (pending_starters.size() > 1) { |  | 
|  288       SCOPED_TRACE(testing::Message() << pending_starters.size() << |  | 
|  289                    " starters left."); |  | 
|  290       for (size_t i = 0; i < pending_starters.size(); ++i) { |  | 
|  291         starters_done_events[i] = |  | 
|  292             &chrome_starters_[pending_starters[i]]->done_event_; |  | 
|  293       } |  | 
|  294       size_t done_index = base::WaitableEvent::WaitMany( |  | 
|  295           starters_done_events, pending_starters.size()); |  | 
|  296       size_t starter_index = pending_starters[done_index]; |  | 
|  297       // If the starter is done but has not marked itself as terminated, |  | 
|  298       // it is because it timed out of its WaitForSingleProcess(). Only the |  | 
|  299       // last one standing should be left waiting... So we failed... |  | 
|  300       EXPECT_TRUE(chrome_starters_[starter_index]->process_terminated_ || |  | 
|  301                   failed) << "There is more than one main process."; |  | 
|  302       if (!chrome_starters_[starter_index]->process_terminated_) { |  | 
|  303         // This will stop the "for kNbAttempts" loop. |  | 
|  304         failed = true; |  | 
|  305         // But we let the last loop turn finish so that we can properly |  | 
|  306         // kill all remaining processes. Starting with this one... |  | 
|  307         if (chrome_starters_[starter_index]->process_handle_ != |  | 
|  308             base::kNullProcessHandle) { |  | 
|  309           KillProcessTree(chrome_starters_[starter_index]->process_handle_); |  | 
|  310         } |  | 
|  311       } |  | 
|  312       pending_starters.erase(pending_starters.begin() + done_index); |  | 
|  313     } |  | 
|  314  |  | 
|  315     // "There can be only one!" :-) |  | 
|  316     ASSERT_EQ(static_cast<size_t>(1), pending_starters.size()); |  | 
|  317     size_t last_index = pending_starters.front(); |  | 
|  318     pending_starters.clear(); |  | 
|  319     if (chrome_starters_[last_index]->process_handle_ != |  | 
|  320         base::kNullProcessHandle) { |  | 
|  321       KillProcessTree(chrome_starters_[last_index]->process_handle_); |  | 
|  322       chrome_starters_[last_index]->done_event_.Wait(); |  | 
|  323     } |  | 
|  324   } |  | 
|  325 } |  | 
|  326  |  | 
|  327 }  // namespace |  | 
| OLD | NEW |