diff options
-rw-r--r-- | chrome/browser/browser_main.cc | 15 | ||||
-rw-r--r-- | chrome/browser/browser_process.h | 4 | ||||
-rw-r--r-- | chrome/browser/browser_process_impl.cc | 23 | ||||
-rw-r--r-- | chrome/browser/browser_process_impl.h | 5 | ||||
-rw-r--r-- | chrome/browser/metrics/thread_watcher.cc | 206 | ||||
-rw-r--r-- | chrome/browser/metrics/thread_watcher.h | 75 | ||||
-rw-r--r-- | chrome/browser/metrics/thread_watcher_unittest.cc | 82 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 2 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 1 | ||||
-rw-r--r-- | chrome/test/testing_browser_process.cc | 4 | ||||
-rw-r--r-- | chrome/test/testing_browser_process.h | 3 | ||||
-rw-r--r-- | tools/valgrind/tsan/suppressions.txt | 21 |
12 files changed, 301 insertions, 140 deletions
diff --git a/chrome/browser/browser_main.cc b/chrome/browser/browser_main.cc index 1622bfa..3e481a2 100644 --- a/chrome/browser/browser_main.cc +++ b/chrome/browser/browser_main.cc @@ -44,6 +44,7 @@ #include "chrome/browser/metrics/histogram_synchronizer.h" #include "chrome/browser/metrics/metrics_log.h" #include "chrome/browser/metrics/metrics_service.h" +#include "chrome/browser/metrics/thread_watcher.h" #include "chrome/browser/net/blob_url_request_job_factory.h" #include "chrome/browser/net/chrome_dns_cert_provenance_checker.h" #include "chrome/browser/net/chrome_dns_cert_provenance_checker_factory.h" @@ -625,6 +626,9 @@ void CreateChildThreads(BrowserProcessImpl* process) { process->process_launcher_thread(); process->cache_thread(); process->io_thread(); + // Create watchdog thread after creating all other threads because it will + // watch the other threads and they must be running. + process->watchdog_thread(); } // Returns the new local state object, guaranteed non-NULL. @@ -1276,6 +1280,11 @@ int BrowserMain(const MainFunctionParams& parameters) { scoped_refptr<HistogramSynchronizer> histogram_synchronizer( new HistogramSynchronizer()); + // Initialize thread watcher system. This is a singleton and is used by + // WatchDogThread to keep track of information about threads that are being + // watched. + scoped_ptr<ThreadWatcherList> thread_watcher_list(new ThreadWatcherList()); + // Initialize the prefs of the local state. browser::RegisterLocalState(local_state); @@ -1309,6 +1318,9 @@ int BrowserMain(const MainFunctionParams& parameters) { CreateChildThreads(browser_process.get()); + // Start watching all browser threads for responsiveness. + ThreadWatcherList::StartWatchingAll(); + #if defined(OS_CHROMEOS) // Now that the file thread exists we can record our stats. chromeos::BootTimesLoader::Get()->RecordChromeMainStats(); @@ -1826,6 +1838,9 @@ int BrowserMain(const MainFunctionParams& parameters) { process_singleton.Cleanup(); + // Stop all tasks that might run on WatchDogThread. + ThreadWatcherList::StopWatchingAll(); + metrics->Stop(); // browser_shutdown takes care of deleting browser_process, so we need to diff --git a/chrome/browser/browser_process.h b/chrome/browser/browser_process.h index 1eb9f03..d406eb0 100644 --- a/chrome/browser/browser_process.h +++ b/chrome/browser/browser_process.h @@ -40,6 +40,7 @@ class ResourceDispatcherHost; class SidebarManager; class TabCloseableStateWatcher; class ThumbnailGenerator; +class WatchDogThread; namespace base { class Thread; @@ -118,6 +119,9 @@ class BrowserProcess { virtual base::Thread* background_x11_thread() = 0; #endif + // Returns the thread that is used for health check of all browser threads. + virtual WatchDogThread* watchdog_thread() = 0; + virtual policy::BrowserPolicyConnector* browser_policy_connector() = 0; virtual IconManager* icon_manager() = 0; diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc index 3a76341..b173c72 100644 --- a/chrome/browser/browser_process_impl.cc +++ b/chrome/browser/browser_process_impl.cc @@ -31,6 +31,7 @@ #include "chrome/browser/intranet_redirect_detector.h" #include "chrome/browser/io_thread.h" #include "chrome/browser/metrics/metrics_service.h" +#include "chrome/browser/metrics/thread_watcher.h" #include "chrome/browser/net/chrome_net_log.h" #include "chrome/browser/net/predictor_api.h" #include "chrome/browser/net/sdch_dictionary_fetcher.h" @@ -98,6 +99,7 @@ BrowserProcessImpl::BrowserProcessImpl(const CommandLine& command_line) created_db_thread_(false), created_process_launcher_thread_(false), created_cache_thread_(false), + created_watchdog_thread_(false), created_profile_manager_(false), created_local_state_(false), created_icon_manager_(false), @@ -248,6 +250,9 @@ BrowserProcessImpl::~BrowserProcessImpl() { // on the db thread too. db_thread_.reset(); + // Stop the watchdog thread after stopping other threads. + watchdog_thread_.reset(); + // At this point, no render process exist and the file, io, db, and // webkit threads in this process have all terminated, so it's safe // to access local state data such as cookies, database, or local storage. @@ -393,6 +398,14 @@ base::Thread* BrowserProcessImpl::background_x11_thread() { } #endif +WatchDogThread* BrowserProcessImpl::watchdog_thread() { + DCHECK(CalledOnValidThread()); + if (!created_watchdog_thread_) + CreateWatchdogThread(); + DCHECK(watchdog_thread_.get() != NULL); + return watchdog_thread_.get(); +} + ProfileManager* BrowserProcessImpl::profile_manager() { DCHECK(CalledOnValidThread()); if (!created_profile_manager_) @@ -734,6 +747,16 @@ void BrowserProcessImpl::CreateCacheThread() { cache_thread_.swap(thread); } +void BrowserProcessImpl::CreateWatchdogThread() { + DCHECK(!created_watchdog_thread_ && watchdog_thread_.get() == NULL); + created_watchdog_thread_ = true; + + scoped_ptr<WatchDogThread> thread(new WatchDogThread()); + if (!thread->Start()) + return; + watchdog_thread_.swap(thread); +} + void BrowserProcessImpl::CreateProfileManager() { DCHECK(!created_profile_manager_ && profile_manager_.get() == NULL); created_profile_manager_ = true; diff --git a/chrome/browser/browser_process_impl.h b/chrome/browser/browser_process_impl.h index 581650a..cccd985 100644 --- a/chrome/browser/browser_process_impl.h +++ b/chrome/browser/browser_process_impl.h @@ -57,6 +57,7 @@ class BrowserProcessImpl : public BrowserProcess, #if defined(USE_X11) virtual base::Thread* background_x11_thread(); #endif + virtual WatchDogThread* watchdog_thread(); virtual ProfileManager* profile_manager(); virtual PrefService* local_state(); virtual DevToolsManager* devtools_manager(); @@ -124,6 +125,7 @@ class BrowserProcessImpl : public BrowserProcess, void CreateDBThread(); void CreateProcessLauncherThread(); void CreateCacheThread(); + void CreateWatchdogThread(); void CreateTemplateURLModel(); void CreateProfileManager(); void CreateWebDataService(); @@ -171,6 +173,9 @@ class BrowserProcessImpl : public BrowserProcess, bool created_cache_thread_; scoped_ptr<base::Thread> cache_thread_; + bool created_watchdog_thread_; + scoped_ptr<WatchDogThread> watchdog_thread_; + bool created_profile_manager_; scoped_ptr<ProfileManager> profile_manager_; diff --git a/chrome/browser/metrics/thread_watcher.cc b/chrome/browser/metrics/thread_watcher.cc index ececcac..b1597f7 100644 --- a/chrome/browser/metrics/thread_watcher.cc +++ b/chrome/browser/metrics/thread_watcher.cc @@ -2,14 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#if 0 - #include "base/threading/thread_restrictions.h" #include "build/build_config.h" #include "chrome/browser/metrics/metrics_service.h" #include "chrome/browser/metrics/thread_watcher.h" #include "chrome/common/notification_service.h" +#if defined(OS_WIN) +#include <Objbase.h> +#endif + // static const int ThreadWatcher::kPingCount = 3; @@ -42,11 +44,10 @@ void ThreadWatcher::StartWatching(const BrowserThread::ID thread_id, DCHECK_GE(sleep_time.InMilliseconds(), 0); DCHECK_GE(unresponsive_time.InMilliseconds(), sleep_time.InMilliseconds()); - // If we are not on WATCHDOG thread, then post a task to call StartWatching on - // WATCHDOG thread. - if (!BrowserThread::CurrentlyOn(BrowserThread::WATCHDOG)) { - BrowserThread::PostTask( - BrowserThread::WATCHDOG, + // If we are not on WatchDogThread, then post a task to call StartWatching on + // WatchDogThread. + if (!WatchDogThread::CurrentlyOnWatchDogThread()) { + WatchDogThread::PostTask( FROM_HERE, NewRunnableFunction( &ThreadWatcher::StartWatching, @@ -54,7 +55,7 @@ void ThreadWatcher::StartWatching(const BrowserThread::ID thread_id, return; } - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WATCHDOG)); + DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); // Create a new thread watcher object for the given thread and activate it. ThreadWatcher* watcher = @@ -64,7 +65,7 @@ void ThreadWatcher::StartWatching(const BrowserThread::ID thread_id, } void ThreadWatcher::ActivateThreadWatching() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WATCHDOG)); + DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); if (active_) return; active_ = true; ping_count_ = kPingCount; @@ -74,14 +75,14 @@ void ThreadWatcher::ActivateThreadWatching() { } void ThreadWatcher::DeActivateThreadWatching() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WATCHDOG)); + DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); active_ = false; ping_count_ = 0; method_factory_.RevokeAll(); } void ThreadWatcher::WakeUp() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WATCHDOG)); + DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); // There is some user activity, PostPingMessage task of thread watcher if // needed. if (!active_) return; @@ -95,7 +96,7 @@ void ThreadWatcher::WakeUp() { } void ThreadWatcher::PostPingMessage() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WATCHDOG)); + DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); // If we have stopped watching or if the user is idle, then stop sending // ping messages. if (!active_ || ping_count_ <= 0) @@ -126,7 +127,7 @@ void ThreadWatcher::PostPingMessage() { } void ThreadWatcher::OnPongMessage(uint64 ping_sequence_number) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WATCHDOG)); + DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); // Record watched thread's response time. base::TimeDelta response_time = base::TimeTicks::Now() - ping_time_; histogram_->AddTime(response_time); @@ -152,7 +153,7 @@ void ThreadWatcher::OnPongMessage(uint64 ping_sequence_number) { } bool ThreadWatcher::OnCheckResponsiveness(uint64 ping_sequence_number) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WATCHDOG)); + DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); // If we have stopped watching then consider thread as responding. if (!active_) return true; @@ -178,7 +179,7 @@ void ThreadWatcher::OnPingMessage(const BrowserThread::ID thread_id, Task* callback_task) { // This method is called on watched thread. DCHECK(BrowserThread::CurrentlyOn(thread_id)); - BrowserThread::PostTask(BrowserThread::WATCHDOG, FROM_HERE, callback_task); + WatchDogThread::PostTask(FROM_HERE, callback_task); } //------------------------------------------------------------------------------ @@ -191,22 +192,15 @@ ThreadWatcherList::ThreadWatcherList() : last_wakeup_time_(base::TimeTicks::Now()) { // Assert we are not running on WATCHDOG thread. Would be ideal to assert we // are on UI thread, but Unit tests are not running on UI thread. - DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::WATCHDOG)); - DCHECK(!global_); + DCHECK(!WatchDogThread::CurrentlyOnWatchDogThread()); + CHECK(!global_); global_ = this; // Register Notifications observer. -#if defined(OS_WIN) MetricsService::SetupNotifications(®istrar_, this); -#endif } ThreadWatcherList::~ThreadWatcherList() { base::AutoLock auto_lock(lock_); - while (!registered_.empty()) { - RegistrationList::iterator it = registered_.begin(); - delete it->second; - registered_.erase(it->first); - } DCHECK(this == global_); global_ = NULL; } @@ -220,30 +214,76 @@ void ThreadWatcherList::Register(ThreadWatcher* watcher) { } // static +void ThreadWatcherList::StartWatchingAll() { + if (!WatchDogThread::CurrentlyOnWatchDogThread()) { + WatchDogThread::PostDelayedTask( + FROM_HERE, + NewRunnableFunction(&ThreadWatcherList::StartWatchingAll), + base::TimeDelta::FromSeconds(10).InMilliseconds()); + return; + } + + DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); + const base::TimeDelta kSleepTime = base::TimeDelta::FromSeconds(5); + const base::TimeDelta kUnresponsiveTime = base::TimeDelta::FromSeconds(10); + if (BrowserThread::IsMessageLoopValid(BrowserThread::UI)) { + ThreadWatcher::StartWatching(BrowserThread::UI, "UI", kSleepTime, + kUnresponsiveTime); + } + if (BrowserThread::IsMessageLoopValid(BrowserThread::IO)) { + ThreadWatcher::StartWatching(BrowserThread::IO, "IO", kSleepTime, + kUnresponsiveTime); + } + if (BrowserThread::IsMessageLoopValid(BrowserThread::DB)) { + ThreadWatcher::StartWatching(BrowserThread::DB, "DB", kSleepTime, + kUnresponsiveTime); + } + if (BrowserThread::IsMessageLoopValid(BrowserThread::FILE)) { + ThreadWatcher::StartWatching(BrowserThread::FILE, "FILE", kSleepTime, + kUnresponsiveTime); + } + if (BrowserThread::IsMessageLoopValid(BrowserThread::CACHE)) { + ThreadWatcher::StartWatching(BrowserThread::CACHE, "CACHE", kSleepTime, + kUnresponsiveTime); + } +} + +// static void ThreadWatcherList::StopWatchingAll() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + // Assert we are not running on WATCHDOG thread. Would be ideal to assert we + // are on UI thread, but Unit tests are not running on UI thread. + DCHECK(!WatchDogThread::CurrentlyOnWatchDogThread()); if (!global_) return; - base::AutoLock auto_lock(global_->lock_); - for (RegistrationList::iterator it = global_->registered_.begin(); - global_->registered_.end() != it; - ++it) - BrowserThread::PostTask( - BrowserThread::WATCHDOG, - FROM_HERE, - NewRunnableMethod( - it->second, &ThreadWatcher::DeActivateThreadWatching)); + + // Remove all notifications for all watched threads. + RemoveNotifications(); + + // Delete all thread watcher objects on WatchDogThread. + WatchDogThread::PostTask( + FROM_HERE, + NewRunnableMethod(global_, &ThreadWatcherList::DeleteAll)); } // static void ThreadWatcherList::RemoveNotifications() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + // Assert we are not running on WATCHDOG thread. Would be ideal to assert we + // are on UI thread, but Unit tests are not running on UI thread. + DCHECK(!WatchDogThread::CurrentlyOnWatchDogThread()); if (!global_) return; -#if defined(OS_WIN) base::AutoLock auto_lock(global_->lock_); global_->registrar_.RemoveAll(); -#endif +} + +void ThreadWatcherList::DeleteAll() { + DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); + base::AutoLock auto_lock(lock_); + while (!registered_.empty()) { + RegistrationList::iterator it = registered_.begin(); + delete it->second; + registered_.erase(it->first); + } } void ThreadWatcherList::Observe(NotificationType type, @@ -260,15 +300,15 @@ void ThreadWatcherList::Observe(NotificationType type, last_wakeup_time_ = now; } } - if (need_to_awaken) - BrowserThread::PostTask( - BrowserThread::WATCHDOG, + if (need_to_awaken) { + WatchDogThread::PostTask( FROM_HERE, NewRunnableMethod(this, &ThreadWatcherList::WakeUpAll)); + } } void ThreadWatcherList::WakeUpAll() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WATCHDOG)); + DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); base::AutoLock auto_lock(lock_); for (RegistrationList::iterator it = global_->registered_.begin(); global_->registered_.end() != it; @@ -294,42 +334,88 @@ ThreadWatcher* ThreadWatcherList::PreLockedFind( //------------------------------------------------------------------------------ // WatchDogThread methods and members. +// static +base::Lock WatchDogThread::lock_; +// static +WatchDogThread* WatchDogThread::watchdog_thread_ = NULL; + // The WatchDogThread object must outlive any tasks posted to the IO thread // before the Quit task. DISABLE_RUNNABLE_METHOD_REFCOUNT(WatchDogThread); -WatchDogThread::WatchDogThread() - : BrowserProcessSubThread(BrowserThread::WATCHDOG) { +WatchDogThread::WatchDogThread() : Thread("WATCHDOG") { } WatchDogThread::~WatchDogThread() { - // Remove all notifications for all watched threads. - ThreadWatcherList::RemoveNotifications(); // We cannot rely on our base class to stop the thread since we want our // CleanUp function to run. Stop(); } +// static +bool WatchDogThread::CurrentlyOnWatchDogThread() { + base::AutoLock lock(lock_); + return watchdog_thread_ && + watchdog_thread_->message_loop() == MessageLoop::current(); +} + +// static +bool WatchDogThread::PostTask(const tracked_objects::Location& from_here, + Task* task) { + return PostTaskHelper(from_here, task, 0); +} + +// static +bool WatchDogThread::PostDelayedTask(const tracked_objects::Location& from_here, + Task* task, + int64 delay_ms) { + return PostTaskHelper(from_here, task, delay_ms); +} + +// static +bool WatchDogThread::PostTaskHelper( + const tracked_objects::Location& from_here, + Task* task, + int64 delay_ms) { + { + base::AutoLock lock(lock_); + + MessageLoop* message_loop = watchdog_thread_ ? + watchdog_thread_->message_loop() : NULL; + if (message_loop) { + message_loop->PostDelayedTask(from_here, task, delay_ms); + return true; + } + } + delete task; + + return false; +} + void WatchDogThread::Init() { // This thread shouldn't be allowed to perform any blocking disk I/O. base::ThreadRestrictions::SetIOAllowed(false); - BrowserProcessSubThread::Init(); - #if defined(OS_WIN) - const base::TimeDelta kSleepTime = base::TimeDelta::FromSeconds(5); - const base::TimeDelta kUnresponsiveTime = base::TimeDelta::FromSeconds(10); - ThreadWatcher::StartWatching(BrowserThread::UI, "UI", kSleepTime, - kUnresponsiveTime); - ThreadWatcher::StartWatching(BrowserThread::IO, "IO", kSleepTime, - kUnresponsiveTime); - ThreadWatcher::StartWatching(BrowserThread::DB, "DB", kSleepTime, - kUnresponsiveTime); - ThreadWatcher::StartWatching(BrowserThread::FILE, "FILE", kSleepTime, - kUnresponsiveTime); - ThreadWatcher::StartWatching(BrowserThread::CACHE, "CACHE", kSleepTime, - kUnresponsiveTime); + // Initializes the COM library on the current thread. + HRESULT result = CoInitialize(NULL); + CHECK(result == S_OK); #endif + + base::AutoLock lock(lock_); + CHECK(!watchdog_thread_); + watchdog_thread_ = this; } -#endif // 0 +void WatchDogThread::CleanUp() { + base::AutoLock lock(lock_); + watchdog_thread_ = NULL; +} + +void WatchDogThread::CleanUpAfterMessageLoopDestruction() { +#if defined(OS_WIN) + // Closes the COM library on the current thread. CoInitialize must + // be balanced by a corresponding call to CoUninitialize. + CoUninitialize(); +#endif +} diff --git a/chrome/browser/metrics/thread_watcher.h b/chrome/browser/metrics/thread_watcher.h index f7765c8..0bd701f 100644 --- a/chrome/browser/metrics/thread_watcher.h +++ b/chrome/browser/metrics/thread_watcher.h @@ -8,14 +8,13 @@ #define CHROME_BROWSER_METRICS_THREAD_WATCHER_H_ #pragma once -#if 0 - #include <map> #include <string> #include <vector> #include "base/basictypes.h" #include "base/gtest_prod_util.h" +#include "base/message_loop.h" #include "base/metrics/histogram.h" #include "base/ref_counted.h" #include "base/scoped_ptr.h" @@ -23,7 +22,6 @@ #include "base/task.h" #include "base/threading/thread.h" #include "base/time.h" -#include "chrome/browser/browser_process_sub_thread.h" #include "chrome/browser/browser_thread.h" #include "chrome/common/notification_observer.h" #include "chrome/common/notification_registrar.h" @@ -81,6 +79,12 @@ class ThreadWatcher { // Returns true if we are montioring the thread. bool active() const { return active_; } + // Returns ping_time_ (used by unit tests). + base::TimeTicks ping_time() const { return ping_time_; } + + // Returns ping_sequence_number_ (used by unit tests). + uint64 ping_sequence_number() const { return ping_sequence_number_; } + protected: // Construct a ThreadWatcher for the given thread_id. sleep_time_ is the // wait time between ping messages. unresponsive_time_ is the wait time after @@ -107,21 +111,21 @@ class ThreadWatcher { // OnPongMessage. It also posts a task (OnCheckResponsiveness) to check // responsiveness of monitored thread that would be called after waiting // unresponsive_time_. - // This method is accessible on WATCHDOG thread. + // This method is accessible on WatchDogThread. virtual void PostPingMessage(); // This method handles a Pong Message from watched thread. It will track the // response time (pong time minus ping time) via histograms. It posts a // PostPingMessage task that would be called after waiting sleep_time_. It // increments ping_sequence_number_ by 1. - // This method is accessible on WATCHDOG thread. + // This method is accessible on WatchDogThread. virtual void OnPongMessage(uint64 ping_sequence_number); // This method will determine if the watched thread is responsive or not. If // the latest ping_sequence_number_ is not same as the ping_sequence_number // that is passed in, then we can assume that watched thread has responded // with a pong message. - // This method is accessible on WATCHDOG thread. + // This method is accessible on WatchDogThread. virtual bool OnCheckResponsiveness(uint64 ping_sequence_number); private: @@ -188,8 +192,9 @@ class ThreadWatcher { //------------------------------------------------------------------------------ // Class with a list of all active thread watchers. A thread watcher is active -// if it has been registered, which includes determing the histogram name. -// Only one instance of this class exists. +// if it has been registered, which includes determing the histogram name. This +// class provides utility functions to start and stop watching all browser +// threads. Only one instance of this class exists. class ThreadWatcherList : public NotificationObserver { public: // A map from BrowserThread to the actual instances. @@ -198,33 +203,46 @@ class ThreadWatcherList : public NotificationObserver { // This singleton holds the global list of registered ThreadWatchers. ThreadWatcherList(); // Destructor deletes all registered ThreadWatcher instances. - ~ThreadWatcherList(); + virtual ~ThreadWatcherList(); // Register() stores a pointer to the given ThreadWatcher in a global map. static void Register(ThreadWatcher* watcher); - // This method posts a task on WATCHDOG thread to RevokeAll tasks and to + // This method posts a task on WatchDogThread to start watching all browser + // threads. + // This method is accessible on UI thread. + static void StartWatchingAll(); + + // This method posts a task on WatchDogThread to RevokeAll tasks and to // deactive thread watching of other threads and tell NotificationService to // stop calling Observe. + // This method is accessible on UI thread. static void StopWatchingAll(); // RemoveAll NotificationTypes that are being observed. + // This method is accessible on UI thread. static void RemoveNotifications(); private: // Allow tests to access our innards for testing purposes. FRIEND_TEST(ThreadWatcherTest, Registration); + // Delete all thread watcher objects and remove them from global map. + // This method is accessible on WatchDogThread. + void DeleteAll(); + // This will ensure that the watching is actively taking place. It will wakeup // all thread watchers every 2 seconds. This is the implementation of // NotificationObserver. When a matching notification is posted to the // notification service, this method is called. + // This method is accessible on UI thread. virtual void Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details); // This will ensure that the watching is actively taking place, and awaken // all thread watchers that are registered. + // This method is accessible on WatchDogThread. virtual void WakeUpAll(); // The Find() method can be used to test to see if a given ThreadWatcher was @@ -250,23 +268,50 @@ class ThreadWatcherList : public NotificationObserver { }; //------------------------------------------------------------------------------ -// Class for WATCHDOG thread and in its Init method, we start watching UI, IO, +// Class for WatchDogThread and in its Init method, we start watching UI, IO, // DB, FILE, CACHED threads. -class WatchDogThread : public BrowserProcessSubThread { +class WatchDogThread : public base::Thread { public: + // Constructor. WatchDogThread(); + + // Destroys the thread and stops the thread. virtual ~WatchDogThread(); + // Callable on any thread. Returns whether you're currently on a + // watchdog_thread_. + static bool CurrentlyOnWatchDogThread(); + + // These are the same methods in message_loop.h, but are guaranteed to either + // get posted to the MessageLoop if it's still alive, or be deleted otherwise. + // They return true iff the watchdog thread existed and the task was posted. + // Note that even if the task is posted, there's no guarantee that it will + // run, since the target thread may already have a Quit message in its queue. + static bool PostTask(const tracked_objects::Location& from_here, Task* task); + static bool PostDelayedTask(const tracked_objects::Location& from_here, + Task* task, + int64 delay_ms); + protected: virtual void Init(); + virtual void CleanUp(); + virtual void CleanUpAfterMessageLoopDestruction(); + + private: + static bool PostTaskHelper( + const tracked_objects::Location& from_here, + Task* task, + int64 delay_ms); + + // This lock protects watchdog_thread_. + static base::Lock lock_; + + static WatchDogThread* watchdog_thread_; // The singleton of this class. DISALLOW_COPY_AND_ASSIGN(WatchDogThread); }; - DISABLE_RUNNABLE_METHOD_REFCOUNT(ThreadWatcher); DISABLE_RUNNABLE_METHOD_REFCOUNT(ThreadWatcherList); -#endif // 0 - #endif // CHROME_BROWSER_METRICS_THREAD_WATCHER_H_ diff --git a/chrome/browser/metrics/thread_watcher_unittest.cc b/chrome/browser/metrics/thread_watcher_unittest.cc index 52a4288..efb978b 100644 --- a/chrome/browser/metrics/thread_watcher_unittest.cc +++ b/chrome/browser/metrics/thread_watcher_unittest.cc @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#if 0 - #include "base/basictypes.h" #include "base/logging.h" #include "base/message_loop.h" @@ -13,6 +11,7 @@ #include "base/synchronization/lock.h" #include "base/threading/platform_thread.h" #include "base/time.h" +#include "build/build_config.h" #include "chrome/browser/metrics/thread_watcher.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" @@ -52,6 +51,8 @@ class CustomThreadWatcher : public ThreadWatcher { uint64 pong_received_; uint64 success_response_; uint64 failed_response_; + base::TimeTicks saved_ping_time_; + uint64 saved_ping_sequence_number_; CustomThreadWatcher(const BrowserThread::ID thread_id, const std::string thread_name, @@ -65,7 +66,9 @@ class CustomThreadWatcher : public ThreadWatcher { ping_sent_(0), pong_received_(0), success_response_(0), - failed_response_(0) { + failed_response_(0), + saved_ping_time_(base::TimeTicks::Now()), + saved_ping_sequence_number_(0) { } State UpdateState(State new_state) { @@ -75,6 +78,12 @@ class CustomThreadWatcher : public ThreadWatcher { old_state = thread_watcher_state_; if (old_state != DEACTIVATED) thread_watcher_state_ = new_state; + if (new_state == SENT_PING) + ++ping_sent_; + if (new_state == RECEIVED_PONG) + ++pong_received_; + saved_ping_time_ = ping_time(); + saved_ping_sequence_number_ = ping_sequence_number(); } state_changed_.Broadcast(); // LOG(INFO) << "UpdateState: thread_name_: " << thread_name_ << @@ -112,11 +121,9 @@ class CustomThreadWatcher : public ThreadWatcher { State old_state = UpdateState(SENT_PING); EXPECT_TRUE(old_state == ACTIVATED || old_state == RECEIVED_PONG); ThreadWatcher::PostPingMessage(); - ++ping_sent_; } void OnPongMessage(uint64 ping_sequence_number) { - ++pong_received_; State old_state = UpdateState(RECEIVED_PONG); EXPECT_TRUE(old_state == SENT_PING || old_state == DEACTIVATED); ThreadWatcher::OnPongMessage(ping_sequence_number); @@ -146,7 +153,7 @@ class CustomThreadWatcher : public ThreadWatcher { } void VeryLongMethod(TimeDelta wait_time) { - DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::WATCHDOG)); + DCHECK(!WatchDogThread::CurrentlyOnWatchDogThread()); TimeTicks end_time = TimeTicks::Now() + wait_time; { base::AutoLock auto_lock(custom_lock_); @@ -161,7 +168,7 @@ class CustomThreadWatcher : public ThreadWatcher { } State WaitForStateChange(const TimeDelta& wait_time, State expected_state) { - DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::WATCHDOG)); + DCHECK(!WatchDogThread::CurrentlyOnWatchDogThread()); UpdateWaitState(STARTED_WAITING); State exit_state; @@ -196,7 +203,7 @@ class CustomThreadWatcher : public ThreadWatcher { CheckResponseState WaitForCheckResponse(const TimeDelta& wait_time, CheckResponseState expected_state) { - DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::WATCHDOG)); + DCHECK(!WatchDogThread::CurrentlyOnWatchDogThread()); UpdateWaitState(STARTED_WAITING); CheckResponseState exit_state; @@ -245,13 +252,9 @@ class ThreadWatcherTest : public ::testing::Test { CustomThreadWatcher* webkit_watcher_; ThreadWatcherTest() { - } - - protected: - virtual void SetUp() { webkit_thread_.reset(new BrowserThread(BrowserThread::WEBKIT)); io_thread_.reset(new BrowserThread(BrowserThread::IO)); - watchdog_thread_.reset(new BrowserThread(BrowserThread::WATCHDOG)); + watchdog_thread_.reset(new WatchDogThread()); webkit_thread_->Start(); io_thread_->Start(); watchdog_thread_->Start(); @@ -268,7 +271,10 @@ class ThreadWatcherTest : public ::testing::Test { webkit_thread_id, webkit_thread_name, kSleepTime, kUnresponsiveTime); } - virtual void TearDown() { + ~ThreadWatcherTest() { + ThreadWatcherList::StopWatchingAll(); + io_watcher_ = NULL; + webkit_watcher_ = NULL; // io_thread_->Stop(); // webkit_thread_->Stop(); // watchdog_thread_->Stop(); @@ -281,7 +287,7 @@ class ThreadWatcherTest : public ::testing::Test { private: scoped_ptr<BrowserThread> webkit_thread_; scoped_ptr<BrowserThread> io_thread_; - scoped_ptr<BrowserThread> watchdog_thread_; + scoped_ptr<WatchDogThread> watchdog_thread_; ThreadWatcherList* thread_watcher_list_; }; @@ -319,14 +325,13 @@ TEST_F(ThreadWatcherTest, Registration) { // Test ActivateThreadWatching and DeActivateThreadWatching of IO thread. This // method also checks that pong message was sent by the watched thread and pong -// message was received by the WATCHDOG thread. It also checks that +// message was received by the WatchDogThread. It also checks that // OnCheckResponsiveness has verified the ping-pong mechanism and the watched // thread is not hung. TEST_F(ThreadWatcherTest, ThreadResponding) { TimeTicks time_before_ping = TimeTicks::Now(); // Activate watching IO thread. - BrowserThread::PostTask( - BrowserThread::WATCHDOG, + WatchDogThread::PostTask( FROM_HERE, NewRunnableMethod(io_watcher_, &ThreadWatcher::ActivateThreadWatching)); @@ -339,8 +344,8 @@ TEST_F(ThreadWatcherTest, ThreadResponding) { EXPECT_GT(io_watcher_->ping_sent_, static_cast<uint64>(0)); EXPECT_GT(io_watcher_->pong_received_, static_cast<uint64>(0)); EXPECT_TRUE(io_watcher_->active()); - EXPECT_GE(io_watcher_->ping_time_, time_before_ping); - EXPECT_GE(io_watcher_->ping_sequence_number_, static_cast<uint64>(0)); + EXPECT_GE(io_watcher_->saved_ping_time_, time_before_ping); + EXPECT_GE(io_watcher_->saved_ping_sequence_number_, static_cast<uint64>(0)); // Verify watched thread is responding with ping/pong messaging. io_watcher_->WaitForCheckResponse( @@ -349,8 +354,7 @@ TEST_F(ThreadWatcherTest, ThreadResponding) { EXPECT_EQ(io_watcher_->failed_response_, static_cast<uint64>(0)); // DeActivate thread watching for shutdown. - BrowserThread::PostTask( - BrowserThread::WATCHDOG, + WatchDogThread::PostTask( FROM_HERE, NewRunnableMethod(io_watcher_, &ThreadWatcher::DeActivateThreadWatching)); } @@ -371,8 +375,7 @@ TEST_F(ThreadWatcherTest, ThreadNotResponding) { kUnresponsiveTime * 10)); // Activate thread watching. - BrowserThread::PostTask( - BrowserThread::WATCHDOG, + WatchDogThread::PostTask( FROM_HERE, NewRunnableMethod(io_watcher_, &ThreadWatcher::ActivateThreadWatching)); @@ -383,8 +386,7 @@ TEST_F(ThreadWatcherTest, ThreadNotResponding) { EXPECT_GT(io_watcher_->failed_response_, static_cast<uint64>(0)); // DeActivate thread watching for shutdown. - BrowserThread::PostTask( - BrowserThread::WATCHDOG, + WatchDogThread::PostTask( FROM_HERE, NewRunnableMethod(io_watcher_, &ThreadWatcher::DeActivateThreadWatching)); } @@ -392,14 +394,13 @@ TEST_F(ThreadWatcherTest, ThreadNotResponding) { // Test watching of multiple threads with all threads not responding. TEST_F(ThreadWatcherTest, MultipleThreadsResponding) { // Check for WEBKIT thread to perform ping/pong messaging. - BrowserThread::PostTask( - BrowserThread::WATCHDOG, + WatchDogThread::PostTask( FROM_HERE, NewRunnableMethod( webkit_watcher_, &ThreadWatcher::ActivateThreadWatching)); + // Check for IO thread to perform ping/pong messaging. - BrowserThread::PostTask( - BrowserThread::WATCHDOG, + WatchDogThread::PostTask( FROM_HERE, NewRunnableMethod(io_watcher_, &ThreadWatcher::ActivateThreadWatching)); @@ -422,12 +423,11 @@ TEST_F(ThreadWatcherTest, MultipleThreadsResponding) { EXPECT_EQ(io_watcher_->failed_response_, static_cast<uint64>(0)); // DeActivate thread watching for shutdown. - BrowserThread::PostTask( - BrowserThread::WATCHDOG, + WatchDogThread::PostTask( FROM_HERE, NewRunnableMethod(io_watcher_, &ThreadWatcher::DeActivateThreadWatching)); - BrowserThread::PostTask( - BrowserThread::WATCHDOG, + + WatchDogThread::PostTask( FROM_HERE, NewRunnableMethod( webkit_watcher_, &ThreadWatcher::DeActivateThreadWatching)); @@ -446,15 +446,13 @@ TEST_F(ThreadWatcherTest, MultipleThreadsNotResponding) { kUnresponsiveTime * 10)); // Activate watching of WEBKIT thread. - BrowserThread::PostTask( - BrowserThread::WATCHDOG, + WatchDogThread::PostTask( FROM_HERE, NewRunnableMethod( webkit_watcher_, &ThreadWatcher::ActivateThreadWatching)); // Activate watching of IO thread. - BrowserThread::PostTask( - BrowserThread::WATCHDOG, + WatchDogThread::PostTask( FROM_HERE, NewRunnableMethod(io_watcher_, &ThreadWatcher::ActivateThreadWatching)); @@ -471,15 +469,11 @@ TEST_F(ThreadWatcherTest, MultipleThreadsNotResponding) { EXPECT_GT(io_watcher_->failed_response_, static_cast<uint64>(0)); // DeActivate thread watching for shutdown. - BrowserThread::PostTask( - BrowserThread::WATCHDOG, + WatchDogThread::PostTask( FROM_HERE, NewRunnableMethod(io_watcher_, &ThreadWatcher::DeActivateThreadWatching)); - BrowserThread::PostTask( - BrowserThread::WATCHDOG, + WatchDogThread::PostTask( FROM_HERE, NewRunnableMethod( webkit_watcher_, &ThreadWatcher::DeActivateThreadWatching)); } - -#endif // 0 diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index db88990..570d442 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1297,6 +1297,8 @@ 'browser/metrics/metrics_response.h', 'browser/metrics/metrics_service.cc', 'browser/metrics/metrics_service.h', + 'browser/metrics/thread_watcher.cc', + 'browser/metrics/thread_watcher.h', 'browser/metrics/user_metrics.cc', 'browser/metrics/user_metrics.h', 'browser/nacl_host/nacl_broker_host_win.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 9a7ef72..ebb1ba7 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1354,6 +1354,7 @@ 'browser/metrics/metrics_log_unittest.cc', 'browser/metrics/metrics_response_unittest.cc', 'browser/metrics/metrics_service_unittest.cc', + 'browser/metrics/thread_watcher_unittest.cc', 'browser/mock_plugin_exceptions_table_model.cc', 'browser/mock_plugin_exceptions_table_model.h', 'browser/net/connection_tester_unittest.cc', diff --git a/chrome/test/testing_browser_process.cc b/chrome/test/testing_browser_process.cc index adb2b10..9a4e72f 100644 --- a/chrome/test/testing_browser_process.cc +++ b/chrome/test/testing_browser_process.cc @@ -58,6 +58,10 @@ base::Thread* TestingBrowserProcess::cache_thread() { return NULL; } +WatchDogThread* TestingBrowserProcess::watchdog_thread() { + return NULL; +} + ProfileManager* TestingBrowserProcess::profile_manager() { return profile_manager_.get(); } diff --git a/chrome/test/testing_browser_process.h b/chrome/test/testing_browser_process.h index f302458..4c4d7ef 100644 --- a/chrome/test/testing_browser_process.h +++ b/chrome/test/testing_browser_process.h @@ -22,6 +22,7 @@ class IOThread; class GoogleURLTracker; class PrefService; +class WatchDogThread; namespace base { class WaitableEvent; @@ -58,6 +59,8 @@ class TestingBrowserProcess : public BrowserProcess { virtual base::Thread* cache_thread(); + virtual WatchDogThread* watchdog_thread(); + virtual ProfileManager* profile_manager(); virtual PrefService* local_state(); diff --git a/tools/valgrind/tsan/suppressions.txt b/tools/valgrind/tsan/suppressions.txt index ef42765..a5a327f 100644 --- a/tools/valgrind/tsan/suppressions.txt +++ b/tools/valgrind/tsan/suppressions.txt @@ -152,13 +152,6 @@ fun:base::RefCounted<ProtocolHandlerRegistry>::* } -{ - bug_73975 - ThreadSanitizer:Race - ... - fun:base::WeakPtrFactory<ThreadWatcher>::* -} - ############################ # Benign races { @@ -383,20 +376,6 @@ fun:base::ThreadCollisionWarner::Leave } -{ - bug_74029a - ThreadSanitzier:Race - ... - fun:CustomThreadWatcher::OnPongMessage* -} - -{ - bug_74029b - ThreadSanitzier:Race - ... - fun:ThreadWatcherTest_ThreadResponding_Test::TestBody* -} - ############################ # Benign races in ICU { |