| Index: chrome/browser/metrics/thread_watcher.cc
|
| ===================================================================
|
| --- chrome/browser/metrics/thread_watcher.cc (revision 89361)
|
| +++ chrome/browser/metrics/thread_watcher.cc (working copy)
|
| @@ -2,10 +2,14 @@
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| +#include <math.h> // ceil
|
| +
|
| +#include "base/string_tokenizer.h"
|
| #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/chrome_switches.h"
|
| #include "content/common/notification_service.h"
|
|
|
| #if defined(OS_WIN)
|
| @@ -15,14 +19,14 @@
|
| // static
|
| const int ThreadWatcher::kPingCount = 6;
|
|
|
| -// static
|
| -const int ThreadWatcher::kUnresponsiveCount = 6;
|
| -
|
| // ThreadWatcher methods and members.
|
| ThreadWatcher::ThreadWatcher(const BrowserThread::ID& thread_id,
|
| const std::string& thread_name,
|
| const base::TimeDelta& sleep_time,
|
| - const base::TimeDelta& unresponsive_time)
|
| + const base::TimeDelta& unresponsive_time,
|
| + uint32 unresponsive_threshold,
|
| + bool crash_on_hang,
|
| + uint32 live_threads_threshold)
|
| : thread_id_(thread_id),
|
| thread_name_(thread_name),
|
| sleep_time_(sleep_time),
|
| @@ -36,7 +40,11 @@
|
| unresponsive_time_histogram_(NULL),
|
| unresponsive_count_(0),
|
| hung_processing_complete_(false),
|
| + unresponsive_threshold_(unresponsive_threshold),
|
| + crash_on_hang_(crash_on_hang),
|
| + live_threads_threshold_(live_threads_threshold),
|
| ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
|
| + DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
|
| Initialize();
|
| }
|
|
|
| @@ -46,7 +54,10 @@
|
| void ThreadWatcher::StartWatching(const BrowserThread::ID& thread_id,
|
| const std::string& thread_name,
|
| const base::TimeDelta& sleep_time,
|
| - const base::TimeDelta& unresponsive_time) {
|
| + const base::TimeDelta& unresponsive_time,
|
| + uint32 unresponsive_threshold,
|
| + bool crash_on_hang,
|
| + uint32 live_threads_threshold) {
|
| DCHECK_GE(sleep_time.InMilliseconds(), 0);
|
| DCHECK_GE(unresponsive_time.InMilliseconds(), sleep_time.InMilliseconds());
|
|
|
| @@ -55,17 +66,27 @@
|
| if (!WatchDogThread::CurrentlyOnWatchDogThread()) {
|
| WatchDogThread::PostTask(
|
| FROM_HERE,
|
| - NewRunnableFunction(
|
| - &ThreadWatcher::StartWatching,
|
| - thread_id, thread_name, sleep_time, unresponsive_time));
|
| + NewRunnableFunction(&ThreadWatcher::StartWatching,
|
| + thread_id,
|
| + thread_name,
|
| + sleep_time,
|
| + unresponsive_time,
|
| + unresponsive_threshold,
|
| + crash_on_hang,
|
| + live_threads_threshold));
|
| return;
|
| }
|
|
|
| DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
|
|
|
| // Create a new thread watcher object for the given thread and activate it.
|
| - ThreadWatcher* watcher =
|
| - new ThreadWatcher(thread_id, thread_name, sleep_time, unresponsive_time);
|
| + ThreadWatcher* watcher = new ThreadWatcher(thread_id,
|
| + thread_name,
|
| + sleep_time,
|
| + unresponsive_time,
|
| + unresponsive_threshold,
|
| + crash_on_hang,
|
| + live_threads_threshold);
|
| DCHECK(watcher);
|
| // If we couldn't register the thread watcher object, we are shutting down,
|
| // then don't activate thread watching.
|
| @@ -196,6 +217,7 @@
|
| }
|
|
|
| void ThreadWatcher::Initialize() {
|
| + DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
|
| ThreadWatcherList::Register(this);
|
|
|
| const std::string response_time_histogram_name =
|
| @@ -244,9 +266,8 @@
|
| void ThreadWatcher::GotNoResponse() {
|
| DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
|
|
|
| - // Record how other threads are responding when we don't get a response for
|
| - // ping message atleast kUnresponsiveCount times.
|
| - if (++unresponsive_count_ < kUnresponsiveCount)
|
| + ++unresponsive_count_;
|
| + if (!IsVeryUnresponsive())
|
| return;
|
|
|
| // Record total unresponsive_time since last pong message.
|
| @@ -257,210 +278,313 @@
|
| if (hung_processing_complete_)
|
| return;
|
|
|
| - int no_of_responding_threads = 0;
|
| - int no_of_unresponding_threads = 0;
|
| - ThreadWatcherList::GetStatusOfThreads(&no_of_responding_threads,
|
| - &no_of_unresponding_threads);
|
| + // Record how other threads are responding.
|
| + uint32 responding_thread_count = 0;
|
| + uint32 unresponding_thread_count = 0;
|
| + ThreadWatcherList::GetStatusOfThreads(&responding_thread_count,
|
| + &unresponding_thread_count);
|
|
|
| // Record how many watched threads are responding.
|
| - responsive_count_histogram_->Add(no_of_responding_threads);
|
| + responsive_count_histogram_->Add(responding_thread_count);
|
|
|
| // Record how many watched threads are not responding.
|
| - unresponsive_count_histogram_->Add(no_of_unresponding_threads);
|
| + unresponsive_count_histogram_->Add(unresponding_thread_count);
|
|
|
| - // Crash the browser if IO thread hasn't responded atleast kUnresponsiveCount
|
| - // times and if the number of other threads is equal to 1. We picked 1 to
|
| - // reduce the number of crashes and to get some sample data.
|
| - if (thread_id_ == BrowserThread::IO && no_of_responding_threads == 1) {
|
| + // Crash the browser if the watched thread is to be crashed on hang and if the
|
| + // number of other threads responding is equal to live_threads_threshold_.
|
| + if (crash_on_hang_ && responding_thread_count == live_threads_threshold_) {
|
| int* crash = NULL;
|
| - CHECK(crash++);
|
| + CHECK(crash + thread_id_);
|
| }
|
|
|
| hung_processing_complete_ = true;
|
| }
|
|
|
| +bool ThreadWatcher::IsVeryUnresponsive() {
|
| + DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
|
| + return unresponsive_count_ >= unresponsive_threshold_;
|
| +}
|
| +
|
| // ThreadWatcherList methods and members.
|
| //
|
| // static
|
| -ThreadWatcherList* ThreadWatcherList::global_ = NULL;
|
| +ThreadWatcherList* ThreadWatcherList::g_thread_watcher_list_ = NULL;
|
| // static
|
| const int ThreadWatcherList::kSleepSeconds = 1;
|
| // static
|
| const int ThreadWatcherList::kUnresponsiveSeconds = 2;
|
| +// static
|
| +const int ThreadWatcherList::kUnresponsiveCount = 6;
|
| +// static
|
| +const int ThreadWatcherList::kLiveThreadsThreshold = 1;
|
|
|
| -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(!WatchDogThread::CurrentlyOnWatchDogThread());
|
| - CHECK(!global_);
|
| - global_ = this;
|
| - // Register Notifications observer.
|
| - MetricsService::SetUpNotifications(®istrar_, this);
|
| +// static
|
| +void ThreadWatcherList::StartWatchingAll(const CommandLine& command_line) {
|
| + ThreadWatcherObserver::SetupNotifications(
|
| + base::TimeDelta::FromSeconds(kSleepSeconds * ThreadWatcher::kPingCount));
|
| +
|
| + uint32 unresponsive_threshold;
|
| + std::set<std::string> crash_on_hang_thread_names;
|
| + uint32 live_threads_threshold;
|
| + ParseCommandLine(command_line,
|
| + &unresponsive_threshold,
|
| + &crash_on_hang_thread_names,
|
| + &live_threads_threshold);
|
| +
|
| + WatchDogThread::PostDelayedTask(
|
| + FROM_HERE,
|
| + NewRunnableFunction(&ThreadWatcherList::InitializeAndStartWatching,
|
| + unresponsive_threshold,
|
| + crash_on_hang_thread_names,
|
| + live_threads_threshold),
|
| + base::TimeDelta::FromSeconds(120).InMilliseconds());
|
| }
|
|
|
| -ThreadWatcherList::~ThreadWatcherList() {
|
| - base::AutoLock auto_lock(lock_);
|
| - DCHECK(this == global_);
|
| - global_ = NULL;
|
| +// static
|
| +void ThreadWatcherList::StopWatchingAll() {
|
| + ThreadWatcherObserver::RemoveNotifications();
|
| + DeleteAll();
|
| }
|
|
|
| // static
|
| void ThreadWatcherList::Register(ThreadWatcher* watcher) {
|
| - if (!global_)
|
| + DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
|
| + if (!g_thread_watcher_list_)
|
| return;
|
| - base::AutoLock auto_lock(global_->lock_);
|
| - DCHECK(!global_->PreLockedFind(watcher->thread_id()));
|
| - global_->registered_[watcher->thread_id()] = watcher;
|
| + DCHECK(!g_thread_watcher_list_->Find(watcher->thread_id()));
|
| + g_thread_watcher_list_->registered_[watcher->thread_id()] = watcher;
|
| }
|
|
|
| // static
|
| bool ThreadWatcherList::IsRegistered(const BrowserThread::ID thread_id) {
|
| + DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
|
| return NULL != ThreadWatcherList::Find(thread_id);
|
| }
|
|
|
| // static
|
| -void ThreadWatcherList::StartWatchingAll() {
|
| - if (!WatchDogThread::CurrentlyOnWatchDogThread()) {
|
| - WatchDogThread::PostDelayedTask(
|
| - FROM_HERE,
|
| - NewRunnableFunction(&ThreadWatcherList::StartWatchingAll),
|
| - base::TimeDelta::FromSeconds(120).InMilliseconds());
|
| +void ThreadWatcherList::GetStatusOfThreads(uint32* responding_thread_count,
|
| + uint32* unresponding_thread_count) {
|
| + DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
|
| + *responding_thread_count = 0;
|
| + *unresponding_thread_count = 0;
|
| + if (!g_thread_watcher_list_)
|
| return;
|
| +
|
| + for (RegistrationList::iterator it =
|
| + g_thread_watcher_list_->registered_.begin();
|
| + g_thread_watcher_list_->registered_.end() != it;
|
| + ++it) {
|
| + if (it->second->IsVeryUnresponsive())
|
| + ++(*unresponding_thread_count);
|
| + else
|
| + ++(*responding_thread_count);
|
| }
|
| +}
|
| +
|
| +// static
|
| +void ThreadWatcherList::WakeUpAll() {
|
| DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
|
| - const base::TimeDelta kSleepTime =
|
| - base::TimeDelta::FromSeconds(kSleepSeconds);
|
| - const base::TimeDelta kUnresponsiveTime =
|
| - base::TimeDelta::FromSeconds(kUnresponsiveSeconds);
|
| - if (BrowserThread::IsMessageLoopValid(BrowserThread::UI)) {
|
| - ThreadWatcher::StartWatching(BrowserThread::UI, "UI", kSleepTime,
|
| - kUnresponsiveTime);
|
| + if (!g_thread_watcher_list_)
|
| + return;
|
| +
|
| + for (RegistrationList::iterator it =
|
| + g_thread_watcher_list_->registered_.begin();
|
| + g_thread_watcher_list_->registered_.end() != it;
|
| + ++it)
|
| + it->second->WakeUp();
|
| +}
|
| +
|
| +ThreadWatcherList::ThreadWatcherList() {
|
| + DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
|
| + CHECK(!g_thread_watcher_list_);
|
| + g_thread_watcher_list_ = this;
|
| +}
|
| +
|
| +ThreadWatcherList::~ThreadWatcherList() {
|
| + DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
|
| + DCHECK(this == g_thread_watcher_list_);
|
| + g_thread_watcher_list_ = NULL;
|
| +}
|
| +
|
| +// static
|
| +void ThreadWatcherList::ParseCommandLine(
|
| + const CommandLine& command_line,
|
| + uint32* unresponsive_threshold,
|
| + std::set<std::string>* crash_on_hang_thread_names,
|
| + uint32* live_threads_threshold) {
|
| + // Determine |unresponsive_threshold| based on switches::kCrashOnHangSeconds.
|
| + *unresponsive_threshold = kUnresponsiveCount;
|
| + std::string crash_on_hang_seconds =
|
| + command_line.GetSwitchValueASCII(switches::kCrashOnHangSeconds);
|
| + if (!crash_on_hang_seconds.empty()) {
|
| + int crash_seconds = atoi(crash_on_hang_seconds.c_str());
|
| + if (crash_seconds > 0) {
|
| + *unresponsive_threshold = static_cast<uint32>(
|
| + ceil(static_cast<float>(crash_seconds) / kUnresponsiveSeconds));
|
| + }
|
| }
|
| - if (BrowserThread::IsMessageLoopValid(BrowserThread::IO)) {
|
| - ThreadWatcher::StartWatching(BrowserThread::IO, "IO", kSleepTime,
|
| - kUnresponsiveTime);
|
| +
|
| + // Default to crashing the browser if UI or IO threads are not responsive.
|
| + std::string crash_on_hang_threads = "UI,IO";
|
| + if (command_line.HasSwitch(switches::kCrashOnHangThreads)) {
|
| + crash_on_hang_threads =
|
| + command_line.GetSwitchValueASCII(switches::kCrashOnHangThreads);
|
| }
|
| - if (BrowserThread::IsMessageLoopValid(BrowserThread::DB)) {
|
| - ThreadWatcher::StartWatching(BrowserThread::DB, "DB", kSleepTime,
|
| - kUnresponsiveTime);
|
| + StringTokenizer tokens(crash_on_hang_threads, ",");
|
| + while (tokens.GetNext())
|
| + crash_on_hang_thread_names->insert(tokens.token());
|
| +
|
| + // Determine |live_threads_threshold| based on switches::kCrashOnLive.
|
| + *live_threads_threshold = kLiveThreadsThreshold;
|
| + if (command_line.HasSwitch(switches::kCrashOnLive)) {
|
| + std::string live_threads =
|
| + command_line.GetSwitchValueASCII(switches::kCrashOnLive);
|
| + *live_threads_threshold = static_cast<uint32>(atoi(live_threads.c_str()));
|
| }
|
| - 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() {
|
| - // 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;
|
| +void ThreadWatcherList::InitializeAndStartWatching(
|
| + uint32 unresponsive_threshold,
|
| + const std::set<std::string>& crash_on_hang_thread_names,
|
| + uint32 live_threads_threshold) {
|
| + DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
|
|
|
| - // Remove all notifications for all watched threads.
|
| - RemoveNotifications();
|
| + ThreadWatcherList* thread_watcher_list = new ThreadWatcherList();
|
| + CHECK(thread_watcher_list);
|
|
|
| - // Delete all thread watcher objects on WatchDogThread.
|
| - WatchDogThread::PostTask(
|
| - FROM_HERE,
|
| - NewRunnableMethod(global_, &ThreadWatcherList::DeleteAll));
|
| -}
|
| + const base::TimeDelta kSleepTime =
|
| + base::TimeDelta::FromSeconds(kSleepSeconds);
|
| + const base::TimeDelta kUnresponsiveTime =
|
| + base::TimeDelta::FromSeconds(kUnresponsiveSeconds);
|
|
|
| -// static
|
| -void ThreadWatcherList::RemoveNotifications() {
|
| - // 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_);
|
| - global_->registrar_.RemoveAll();
|
| + StartWatching(BrowserThread::UI, "UI", kSleepTime, kUnresponsiveTime,
|
| + unresponsive_threshold, crash_on_hang_thread_names,
|
| + live_threads_threshold);
|
| + StartWatching(BrowserThread::IO, "IO", kSleepTime, kUnresponsiveTime,
|
| + unresponsive_threshold, crash_on_hang_thread_names,
|
| + live_threads_threshold);
|
| + StartWatching(BrowserThread::DB, "DB", kSleepTime, kUnresponsiveTime,
|
| + unresponsive_threshold, crash_on_hang_thread_names,
|
| + live_threads_threshold);
|
| + StartWatching(BrowserThread::FILE, "FILE", kSleepTime, kUnresponsiveTime,
|
| + unresponsive_threshold, crash_on_hang_thread_names,
|
| + live_threads_threshold);
|
| + StartWatching(BrowserThread::CACHE, "CACHE", kSleepTime, kUnresponsiveTime,
|
| + unresponsive_threshold, crash_on_hang_thread_names,
|
| + live_threads_threshold);
|
| }
|
|
|
| // static
|
| -void ThreadWatcherList::GetStatusOfThreads(int* no_of_responding_threads,
|
| - int* no_of_unresponding_threads) {
|
| +void ThreadWatcherList::StartWatching(
|
| + const BrowserThread::ID& thread_id,
|
| + const std::string& thread_name,
|
| + const base::TimeDelta& sleep_time,
|
| + const base::TimeDelta& unresponsive_time,
|
| + uint32 unresponsive_threshold,
|
| + const std::set<std::string>& crash_on_hang_thread_names,
|
| + uint32 live_threads_threshold) {
|
| DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
|
| - *no_of_responding_threads = 0;
|
| - *no_of_unresponding_threads = 0;
|
| - if (!global_)
|
| +
|
| + if (!BrowserThread::IsMessageLoopValid(thread_id))
|
| return;
|
|
|
| - base::AutoLock auto_lock(global_->lock_);
|
| - for (RegistrationList::iterator it = global_->registered_.begin();
|
| - global_->registered_.end() != it;
|
| - ++it) {
|
| - if (it->second->unresponsive_count_ < ThreadWatcher::kUnresponsiveCount)
|
| - ++(*no_of_responding_threads);
|
| - else
|
| - ++(*no_of_unresponding_threads);
|
| - }
|
| + std::set<std::string>::const_iterator it =
|
| + crash_on_hang_thread_names.find(thread_name);
|
| + bool crash_on_hang = (it != crash_on_hang_thread_names.end());
|
| +
|
| + ThreadWatcher::StartWatching(thread_id,
|
| + thread_name,
|
| + sleep_time,
|
| + unresponsive_time,
|
| + unresponsive_threshold,
|
| + crash_on_hang,
|
| + live_threads_threshold);
|
| }
|
|
|
| +// static
|
| 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,
|
| - const NotificationSource& source,
|
| - const NotificationDetails& details) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - // There is some user activity, see if thread watchers are to be awakened.
|
| - bool need_to_awaken = false;
|
| - base::TimeTicks now = base::TimeTicks::Now();
|
| - {
|
| - base::AutoLock lock(lock_);
|
| - if (now - last_wakeup_time_ > base::TimeDelta::FromSeconds(kSleepSeconds)) {
|
| - need_to_awaken = true;
|
| - last_wakeup_time_ = now;
|
| - }
|
| - }
|
| - if (need_to_awaken) {
|
| + if (!WatchDogThread::CurrentlyOnWatchDogThread()) {
|
| WatchDogThread::PostTask(
|
| FROM_HERE,
|
| - NewRunnableMethod(this, &ThreadWatcherList::WakeUpAll));
|
| + NewRunnableFunction(&ThreadWatcherList::DeleteAll));
|
| + return;
|
| }
|
| -}
|
|
|
| -void ThreadWatcherList::WakeUpAll() {
|
| DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
|
| - if (!global_)
|
| + if (!g_thread_watcher_list_)
|
| return;
|
| - base::AutoLock auto_lock(lock_);
|
| - for (RegistrationList::iterator it = global_->registered_.begin();
|
| - global_->registered_.end() != it;
|
| - ++it)
|
| - it->second->WakeUp();
|
| +
|
| + // Delete all thread watcher objects.
|
| + while (!g_thread_watcher_list_->registered_.empty()) {
|
| + RegistrationList::iterator it = g_thread_watcher_list_->registered_.begin();
|
| + delete it->second;
|
| + g_thread_watcher_list_->registered_.erase(it);
|
| + }
|
| +
|
| + delete g_thread_watcher_list_;
|
| }
|
|
|
| // static
|
| ThreadWatcher* ThreadWatcherList::Find(const BrowserThread::ID& thread_id) {
|
| - if (!global_)
|
| + DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
|
| + if (!g_thread_watcher_list_)
|
| return NULL;
|
| - base::AutoLock auto_lock(global_->lock_);
|
| - return global_->PreLockedFind(thread_id);
|
| -}
|
| -
|
| -ThreadWatcher* ThreadWatcherList::PreLockedFind(
|
| - const BrowserThread::ID& thread_id) {
|
| - RegistrationList::iterator it = registered_.find(thread_id);
|
| - if (registered_.end() == it)
|
| + RegistrationList::iterator it =
|
| + g_thread_watcher_list_->registered_.find(thread_id);
|
| + if (g_thread_watcher_list_->registered_.end() == it)
|
| return NULL;
|
| return it->second;
|
| }
|
|
|
| +// ThreadWatcherObserver methods and members.
|
| +//
|
| +// static
|
| +ThreadWatcherObserver* ThreadWatcherObserver::g_thread_watcher_observer_ = NULL;
|
| +
|
| +ThreadWatcherObserver::ThreadWatcherObserver(
|
| + const base::TimeDelta& wakeup_interval)
|
| + : last_wakeup_time_(base::TimeTicks::Now()),
|
| + wakeup_interval_(wakeup_interval) {
|
| + CHECK(!g_thread_watcher_observer_);
|
| + g_thread_watcher_observer_ = this;
|
| +}
|
| +
|
| +ThreadWatcherObserver::~ThreadWatcherObserver() {
|
| + DCHECK(this == g_thread_watcher_observer_);
|
| + g_thread_watcher_observer_ = NULL;
|
| +}
|
| +
|
| +// static
|
| +void ThreadWatcherObserver::SetupNotifications(
|
| + const base::TimeDelta& wakeup_interval) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + ThreadWatcherObserver* observer = new ThreadWatcherObserver(wakeup_interval);
|
| + MetricsService::SetUpNotifications(&observer->registrar_, observer);
|
| +}
|
| +
|
| +// static
|
| +void ThreadWatcherObserver::RemoveNotifications() {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + if (!g_thread_watcher_observer_)
|
| + return;
|
| + g_thread_watcher_observer_->registrar_.RemoveAll();
|
| + delete g_thread_watcher_observer_;
|
| +}
|
| +
|
| +void ThreadWatcherObserver::Observe(NotificationType type,
|
| + const NotificationSource& source,
|
| + const NotificationDetails& details) {
|
| + // There is some user activity, see if thread watchers are to be awakened.
|
| + base::TimeTicks now = base::TimeTicks::Now();
|
| + if ((now - last_wakeup_time_) < wakeup_interval_)
|
| + return;
|
| + last_wakeup_time_ = now;
|
| + WatchDogThread::PostTask(
|
| + FROM_HERE,
|
| + NewRunnableFunction(&ThreadWatcherList::WakeUpAll));
|
| +}
|
| +
|
| // WatchDogThread methods and members.
|
| //
|
| // static
|
| @@ -543,8 +667,8 @@
|
|
|
| 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.
|
| + // Closes the COM library on the current thread. CoInitialize must be balanced
|
| + // by a corresponding call to CoUninitialize.
|
| CoUninitialize();
|
| #endif
|
| }
|
|
|