diff options
author | bulach@chromium.org <bulach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-10 18:10:04 +0000 |
---|---|---|
committer | bulach@chromium.org <bulach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-10 18:10:04 +0000 |
commit | 7404337fae4b1793ab8aed890ddbdb48ade9f846 (patch) | |
tree | 5aec7a4b0669a456f222c7428e65a5a0ec2e0b87 | |
parent | 547969add0c0d1975f6cb1be42a0ae359a2ae0ef (diff) | |
download | chromium_src-7404337fae4b1793ab8aed890ddbdb48ade9f846.zip chromium_src-7404337fae4b1793ab8aed890ddbdb48ade9f846.tar.gz chromium_src-7404337fae4b1793ab8aed890ddbdb48ade9f846.tar.bz2 |
ThreadWatcher: fixes Start/StopWatchingAll.
Start is deferred in relation to Stop, and when they're both called
within the Start interval, the end result was started.
Relands crrev.com/255322, fixing race condition in the test.
BUG=347887,349987
Review URL: https://codereview.chromium.org/188653003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@255974 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/metrics/thread_watcher.cc | 21 | ||||
-rw-r--r-- | chrome/browser/metrics/thread_watcher.h | 15 | ||||
-rw-r--r-- | chrome/browser/metrics/thread_watcher_unittest.cc | 107 |
3 files changed, 141 insertions, 2 deletions
diff --git a/chrome/browser/metrics/thread_watcher.cc b/chrome/browser/metrics/thread_watcher.cc index 1737593..8246b3e 100644 --- a/chrome/browser/metrics/thread_watcher.cc +++ b/chrome/browser/metrics/thread_watcher.cc @@ -409,6 +409,8 @@ bool ThreadWatcher::IsVeryUnresponsive() { // static ThreadWatcherList* ThreadWatcherList::g_thread_watcher_list_ = NULL; // static +bool ThreadWatcherList::g_stopped_ = false; +// static const int ThreadWatcherList::kSleepSeconds = 1; // static const int ThreadWatcherList::kUnresponsiveSeconds = 2; @@ -443,6 +445,10 @@ void ThreadWatcherList::StartWatchingAll(const CommandLine& command_line) { ThreadWatcherObserver::SetupNotifications( base::TimeDelta::FromSeconds(kSleepSeconds * unresponsive_threshold)); + WatchDogThread::PostTask( + FROM_HERE, + base::Bind(&ThreadWatcherList::SetStopped, false)); + WatchDogThread::PostDelayedTask( FROM_HERE, base::Bind(&ThreadWatcherList::InitializeAndStartWatching, @@ -635,6 +641,12 @@ void ThreadWatcherList::InitializeAndStartWatching( const CrashOnHangThreadMap& crash_on_hang_threads) { DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); + // This method is deferred in relationship to its StopWatchingAll() + // counterpart. If a previous initialization has already happened, or if + // stop has been called, there's nothing left to do here. + if (g_thread_watcher_list_ || g_stopped_) + return; + ThreadWatcherList* thread_watcher_list = new ThreadWatcherList(); CHECK(thread_watcher_list); @@ -700,6 +712,9 @@ void ThreadWatcherList::DeleteAll() { } DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); + + SetStopped(true); + if (!g_thread_watcher_list_) return; @@ -725,6 +740,12 @@ ThreadWatcher* ThreadWatcherList::Find(const BrowserThread::ID& thread_id) { return it->second; } +// static +void ThreadWatcherList::SetStopped(bool stopped) { + DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); + g_stopped_ = stopped; +} + // ThreadWatcherObserver methods and members. // // static diff --git a/chrome/browser/metrics/thread_watcher.h b/chrome/browser/metrics/thread_watcher.h index 2c4b027..94d5248 100644 --- a/chrome/browser/metrics/thread_watcher.h +++ b/chrome/browser/metrics/thread_watcher.h @@ -406,12 +406,14 @@ class ThreadWatcherList { private: // Allow tests to access our innards for testing purposes. friend class CustomThreadWatcher; + friend class ThreadWatcherListTest; friend class ThreadWatcherTest; + FRIEND_TEST_ALL_PREFIXES(ThreadWatcherAndroidTest, + ApplicationStatusNotification); + FRIEND_TEST_ALL_PREFIXES(ThreadWatcherListTest, Restart); FRIEND_TEST_ALL_PREFIXES(ThreadWatcherTest, ThreadNamesOnlyArgs); FRIEND_TEST_ALL_PREFIXES(ThreadWatcherTest, ThreadNamesAndLiveThresholdArgs); FRIEND_TEST_ALL_PREFIXES(ThreadWatcherTest, CrashOnHangThreadsAllArgs); - FRIEND_TEST_ALL_PREFIXES(ThreadWatcherAndroidTest, - ApplicationStatusNotification); // This singleton holds the global list of registered ThreadWatchers. ThreadWatcherList(); @@ -464,10 +466,19 @@ class ThreadWatcherList { // already registered, or to retrieve a pointer to it from the global map. static ThreadWatcher* Find(const content::BrowserThread::ID& thread_id); + // Sets |g_stopped_| on the WatchDogThread. This is necessary to reflect the + // state between the delayed |StartWatchingAll| and the immediate + // |StopWatchingAll|. + static void SetStopped(bool stopped); + // The singleton of this class and is used to keep track of information about // threads that are being watched. static ThreadWatcherList* g_thread_watcher_list_; + // StartWatchingAll() is delayed in relation to StopWatchingAll(), so if + // a Stop comes first, prevent further initialization. + static bool g_stopped_; + // This is the wait time between ping messages. static const int kSleepSeconds; diff --git a/chrome/browser/metrics/thread_watcher_unittest.cc b/chrome/browser/metrics/thread_watcher_unittest.cc index 4b799e9..d51d773 100644 --- a/chrome/browser/metrics/thread_watcher_unittest.cc +++ b/chrome/browser/metrics/thread_watcher_unittest.cc @@ -10,6 +10,7 @@ #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop_proxy.h" +#include "base/run_loop.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/string_tokenizer.h" @@ -629,3 +630,109 @@ TEST_F(ThreadWatcherTest, MultipleThreadsNotResponding) { // Wait for the io_watcher_'s VeryLongMethod to finish. io_watcher_->WaitForWaitStateChange(kUnresponsiveTime * 10, ALL_DONE); } + +class ThreadWatcherListTest : public ::testing::Test { + protected: + ThreadWatcherListTest() + : done_(&lock_), + state_available_(false), + has_thread_watcher_list_(false), + stopped_(false) { + } + + void ReadStateOnWatchDogThread() { + CHECK(WatchDogThread::CurrentlyOnWatchDogThread()); + { + base::AutoLock auto_lock(lock_); + has_thread_watcher_list_ = + ThreadWatcherList::g_thread_watcher_list_ != NULL; + stopped_ = ThreadWatcherList::g_stopped_; + state_available_ = true; + } + done_.Signal(); + } + + void CheckState(bool has_thread_watcher_list, + bool stopped, + const char* const msg) { + CHECK(!WatchDogThread::CurrentlyOnWatchDogThread()); + { + base::AutoLock auto_lock(lock_); + state_available_ = false; + } + + WatchDogThread::PostTask( + FROM_HERE, + base::Bind(&ThreadWatcherListTest::ReadStateOnWatchDogThread, + base::Unretained(this))); + { + base::AutoLock auto_lock(lock_); + while (!state_available_) + done_.Wait(); + + EXPECT_EQ(has_thread_watcher_list, has_thread_watcher_list_) << msg; + EXPECT_EQ(stopped, stopped_) << msg; + } + } + + base::Lock lock_; + base::ConditionVariable done_; + + bool state_available_; + bool has_thread_watcher_list_; + bool stopped_; +}; + +TEST_F(ThreadWatcherListTest, Restart) { + ThreadWatcherList::g_initialize_delay_seconds = 1; + + base::MessageLoopForUI message_loop_for_ui; + content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop_for_ui); + + scoped_ptr<WatchDogThread> watchdog_thread_(new WatchDogThread()); + watchdog_thread_->Start(); + + // See http://crbug.com/347887. + // StartWatchingAll() will PostDelayedTask to create g_thread_watcher_list_, + // whilst StopWatchingAll() will just PostTask to destroy it. + // Ensure that when Stop is called, Start will NOT create + // g_thread_watcher_list_ later on. + ThreadWatcherList::StartWatchingAll(*CommandLine::ForCurrentProcess()); + ThreadWatcherList::StopWatchingAll(); + message_loop_for_ui.PostDelayedTask( + FROM_HERE, + message_loop_for_ui.QuitClosure(), + base::TimeDelta::FromSeconds( + ThreadWatcherList::g_initialize_delay_seconds)); + message_loop_for_ui.Run(); + + CheckState(false /* has_thread_watcher_list */, + true /* stopped */, + "Start / Stopped"); + + // Proceed with just |StartWatchingAll| and ensure it'll be started. + ThreadWatcherList::StartWatchingAll(*CommandLine::ForCurrentProcess()); + message_loop_for_ui.PostDelayedTask( + FROM_HERE, + message_loop_for_ui.QuitClosure(), + base::TimeDelta::FromSeconds( + ThreadWatcherList::g_initialize_delay_seconds + 1)); + message_loop_for_ui.Run(); + + CheckState(true /* has_thread_watcher_list */, + false /* stopped */, + "Started"); + + // Finally, StopWatchingAll() must stop. + ThreadWatcherList::StopWatchingAll(); + message_loop_for_ui.PostDelayedTask( + FROM_HERE, + message_loop_for_ui.QuitClosure(), + base::TimeDelta::FromSeconds( + ThreadWatcherList::g_initialize_delay_seconds)); + message_loop_for_ui.Run(); + + CheckState(false /* has_thread_watcher_list */, + true /* stopped */, + "Stopped"); +} |