1 // Copyright (c) 2011 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 #include "base/threading/thread_restrictions.h" 6 #include "build/build_config.h" 7 #include "chrome/browser/metrics/metrics_service.h" 8 #include "chrome/browser/metrics/thread_watcher.h" 9 #include "content/common/notification_service.h" 10 11 #if defined(OS_WIN) 12 #include <Objbase.h> 13 #endif 14 15 // static 16 const int ThreadWatcher::kPingCount = 3; 17 18 // ThreadWatcher methods and members. 19 ThreadWatcher::ThreadWatcher(const BrowserThread::ID& thread_id, 20 const std::string& thread_name, 21 const base::TimeDelta& sleep_time, 22 const base::TimeDelta& unresponsive_time) 23 : thread_id_(thread_id), 24 thread_name_(thread_name), 25 sleep_time_(sleep_time), 26 unresponsive_time_(unresponsive_time), 27 ping_time_(base::TimeTicks::Now()), 28 ping_sequence_number_(0), 29 active_(false), 30 ping_count_(kPingCount), 31 histogram_(NULL), 32 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { 33 Initialize(); 34 } 35 36 ThreadWatcher::~ThreadWatcher() {} 37 38 // static 39 void ThreadWatcher::StartWatching(const BrowserThread::ID& thread_id, 40 const std::string& thread_name, 41 const base::TimeDelta& sleep_time, 42 const base::TimeDelta& unresponsive_time) { 43 DCHECK_GE(sleep_time.InMilliseconds(), 0); 44 DCHECK_GE(unresponsive_time.InMilliseconds(), sleep_time.InMilliseconds()); 45 46 // If we are not on WatchDogThread, then post a task to call StartWatching on 47 // WatchDogThread. 48 if (!WatchDogThread::CurrentlyOnWatchDogThread()) { 49 WatchDogThread::PostTask( 50 FROM_HERE, 51 NewRunnableFunction( 52 &ThreadWatcher::StartWatching, 53 thread_id, thread_name, sleep_time, unresponsive_time)); 54 return; 55 } 56 57 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); 58 59 // Create a new thread watcher object for the given thread and activate it. 60 ThreadWatcher* watcher = 61 new ThreadWatcher(thread_id, thread_name, sleep_time, unresponsive_time); 62 DCHECK(watcher); 63 // If we couldn't register the thread watcher object, we are shutting down, 64 // then don't activate thread watching. 65 if (!ThreadWatcherList::IsRegistered(thread_id)) 66 return; 67 watcher->ActivateThreadWatching(); 68 } 69 70 void ThreadWatcher::ActivateThreadWatching() { 71 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); 72 if (active_) return; 73 active_ = true; 74 ping_count_ = kPingCount; 75 MessageLoop::current()->PostTask( 76 FROM_HERE, 77 method_factory_.NewRunnableMethod(&ThreadWatcher::PostPingMessage)); 78 } 79 80 void ThreadWatcher::DeActivateThreadWatching() { 81 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); 82 active_ = false; 83 ping_count_ = 0; 84 method_factory_.RevokeAll(); 85 } 86 87 void ThreadWatcher::WakeUp() { 88 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); 89 // There is some user activity, PostPingMessage task of thread watcher if 90 // needed. 91 if (!active_) return; 92 93 if (ping_count_ <= 0) { 94 ping_count_ = kPingCount; 95 PostPingMessage(); 96 } else { 97 ping_count_ = kPingCount; 98 } 99 } 100 101 void ThreadWatcher::PostPingMessage() { 102 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); 103 // If we have stopped watching or if the user is idle, then stop sending 104 // ping messages. 105 if (!active_ || ping_count_ <= 0) 106 return; 107 108 // Save the current time when we have sent ping message. 109 ping_time_ = base::TimeTicks::Now(); 110 111 // Send a ping message to the watched thread. 112 Task* callback_task = method_factory_.NewRunnableMethod( 113 &ThreadWatcher::OnPongMessage, ping_sequence_number_); 114 if (BrowserThread::PostTask( 115 thread_id(), 116 FROM_HERE, 117 NewRunnableFunction( 118 &ThreadWatcher::OnPingMessage, thread_id_, callback_task))) { 119 // Post a task to check the responsiveness of watched thread. 120 MessageLoop::current()->PostDelayedTask( 121 FROM_HERE, 122 method_factory_.NewRunnableMethod( 123 &ThreadWatcher::OnCheckResponsiveness, ping_sequence_number_), 124 unresponsive_time_.InMilliseconds()); 125 } else { 126 // Watched thread might have gone away, stop watching it. 127 delete callback_task; 128 DeActivateThreadWatching(); 129 } 130 } 131 132 void ThreadWatcher::OnPongMessage(uint64 ping_sequence_number) { 133 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); 134 // Record watched thread's response time. 135 base::TimeDelta response_time = base::TimeTicks::Now() - ping_time_; 136 histogram_->AddTime(response_time); 137 138 // Check if there are any extra pings in flight. 139 DCHECK_EQ(ping_sequence_number_, ping_sequence_number); 140 if (ping_sequence_number_ != ping_sequence_number) 141 return; 142 143 // Increment sequence number for the next ping message to indicate watched 144 // thread is responsive. 145 ++ping_sequence_number_; 146 147 // If we have stopped watching or if the user is idle, then stop sending 148 // ping messages. 149 if (!active_ || --ping_count_ <= 0) 150 return; 151 152 MessageLoop::current()->PostDelayedTask( 153 FROM_HERE, 154 method_factory_.NewRunnableMethod(&ThreadWatcher::PostPingMessage), 155 sleep_time_.InMilliseconds()); 156 } 157 158 bool ThreadWatcher::OnCheckResponsiveness(uint64 ping_sequence_number) { 159 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); 160 // If we have stopped watching then consider thread as responding. 161 if (!active_) 162 return true; 163 // If the latest ping_sequence_number_ is not same as the ping_sequence_number 164 // that is passed in, then we can assume OnPongMessage was called. 165 // OnPongMessage increments ping_sequence_number_. 166 return ping_sequence_number_ != ping_sequence_number; 167 } 168 169 void ThreadWatcher::Initialize() { 170 ThreadWatcherList::Register(this); 171 const std::string histogram_name = 172 "ThreadWatcher.ResponseTime." + thread_name_; 173 histogram_ = base::Histogram::FactoryTimeGet( 174 histogram_name, 175 base::TimeDelta::FromMilliseconds(1), 176 base::TimeDelta::FromSeconds(100), 50, 177 base::Histogram::kUmaTargetedHistogramFlag); 178 } 179 180 // static 181 void ThreadWatcher::OnPingMessage(const BrowserThread::ID& thread_id, 182 Task* callback_task) { 183 // This method is called on watched thread. 184 DCHECK(BrowserThread::CurrentlyOn(thread_id)); 185 WatchDogThread::PostTask(FROM_HERE, callback_task); 186 } 187 188 // ThreadWatcherList methods and members. 189 // 190 // static 191 ThreadWatcherList* ThreadWatcherList::global_ = NULL; 192 193 ThreadWatcherList::ThreadWatcherList() 194 : last_wakeup_time_(base::TimeTicks::Now()) { 195 // Assert we are not running on WATCHDOG thread. Would be ideal to assert we 196 // are on UI thread, but Unit tests are not running on UI thread. 197 DCHECK(!WatchDogThread::CurrentlyOnWatchDogThread()); 198 CHECK(!global_); 199 global_ = this; 200 // Register Notifications observer. 201 MetricsService::SetUpNotifications(®istrar_, this); 202 } 203 204 ThreadWatcherList::~ThreadWatcherList() { 205 base::AutoLock auto_lock(lock_); 206 DCHECK(this == global_); 207 global_ = NULL; 208 } 209 210 // static 211 void ThreadWatcherList::Register(ThreadWatcher* watcher) { 212 if (!global_) 213 return; 214 base::AutoLock auto_lock(global_->lock_); 215 DCHECK(!global_->PreLockedFind(watcher->thread_id())); 216 global_->registered_[watcher->thread_id()] = watcher; 217 } 218 219 // static 220 bool ThreadWatcherList::IsRegistered(const BrowserThread::ID thread_id) { 221 return NULL != ThreadWatcherList::Find(thread_id); 222 } 223 224 // static 225 void ThreadWatcherList::StartWatchingAll() { 226 if (!WatchDogThread::CurrentlyOnWatchDogThread()) { 227 WatchDogThread::PostDelayedTask( 228 FROM_HERE, 229 NewRunnableFunction(&ThreadWatcherList::StartWatchingAll), 230 base::TimeDelta::FromSeconds(5).InMilliseconds()); 231 return; 232 } 233 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); 234 const base::TimeDelta kSleepTime = base::TimeDelta::FromSeconds(5); 235 const base::TimeDelta kUnresponsiveTime = base::TimeDelta::FromSeconds(10); 236 if (BrowserThread::IsMessageLoopValid(BrowserThread::UI)) { 237 ThreadWatcher::StartWatching(BrowserThread::UI, "UI", kSleepTime, 238 kUnresponsiveTime); 239 } 240 if (BrowserThread::IsMessageLoopValid(BrowserThread::IO)) { 241 ThreadWatcher::StartWatching(BrowserThread::IO, "IO", kSleepTime, 242 kUnresponsiveTime); 243 } 244 if (BrowserThread::IsMessageLoopValid(BrowserThread::DB)) { 245 ThreadWatcher::StartWatching(BrowserThread::DB, "DB", kSleepTime, 246 kUnresponsiveTime); 247 } 248 if (BrowserThread::IsMessageLoopValid(BrowserThread::FILE)) { 249 ThreadWatcher::StartWatching(BrowserThread::FILE, "FILE", kSleepTime, 250 kUnresponsiveTime); 251 } 252 if (BrowserThread::IsMessageLoopValid(BrowserThread::CACHE)) { 253 ThreadWatcher::StartWatching(BrowserThread::CACHE, "CACHE", kSleepTime, 254 kUnresponsiveTime); 255 } 256 } 257 258 // static 259 void ThreadWatcherList::StopWatchingAll() { 260 // Assert we are not running on WATCHDOG thread. Would be ideal to assert we 261 // are on UI thread, but Unit tests are not running on UI thread. 262 DCHECK(!WatchDogThread::CurrentlyOnWatchDogThread()); 263 if (!global_) 264 return; 265 266 // Remove all notifications for all watched threads. 267 RemoveNotifications(); 268 269 // Delete all thread watcher objects on WatchDogThread. 270 WatchDogThread::PostTask( 271 FROM_HERE, 272 NewRunnableMethod(global_, &ThreadWatcherList::DeleteAll)); 273 } 274 275 // static 276 void ThreadWatcherList::RemoveNotifications() { 277 // Assert we are not running on WATCHDOG thread. Would be ideal to assert we 278 // are on UI thread, but Unit tests are not running on UI thread. 279 DCHECK(!WatchDogThread::CurrentlyOnWatchDogThread()); 280 if (!global_) 281 return; 282 base::AutoLock auto_lock(global_->lock_); 283 global_->registrar_.RemoveAll(); 284 } 285 286 void ThreadWatcherList::DeleteAll() { 287 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); 288 base::AutoLock auto_lock(lock_); 289 while (!registered_.empty()) { 290 RegistrationList::iterator it = registered_.begin(); 291 delete it->second; 292 registered_.erase(it->first); 293 } 294 } 295 296 void ThreadWatcherList::Observe(NotificationType type, 297 const NotificationSource& source, 298 const NotificationDetails& details) { 299 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 300 // There is some user activity, see if thread watchers are to be awakened. 301 bool need_to_awaken = false; 302 base::TimeTicks now = base::TimeTicks::Now(); 303 { 304 base::AutoLock lock(lock_); 305 if (now - last_wakeup_time_ > base::TimeDelta::FromSeconds(2)) { 306 need_to_awaken = true; 307 last_wakeup_time_ = now; 308 } 309 } 310 if (need_to_awaken) { 311 WatchDogThread::PostTask( 312 FROM_HERE, 313 NewRunnableMethod(this, &ThreadWatcherList::WakeUpAll)); 314 } 315 } 316 317 void ThreadWatcherList::WakeUpAll() { 318 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); 319 if (!global_) 320 return; 321 base::AutoLock auto_lock(lock_); 322 for (RegistrationList::iterator it = global_->registered_.begin(); 323 global_->registered_.end() != it; 324 ++it) 325 it->second->WakeUp(); 326 } 327 328 // static 329 ThreadWatcher* ThreadWatcherList::Find(const BrowserThread::ID& thread_id) { 330 if (!global_) 331 return NULL; 332 base::AutoLock auto_lock(global_->lock_); 333 return global_->PreLockedFind(thread_id); 334 } 335 336 ThreadWatcher* ThreadWatcherList::PreLockedFind( 337 const BrowserThread::ID& thread_id) { 338 RegistrationList::iterator it = registered_.find(thread_id); 339 if (registered_.end() == it) 340 return NULL; 341 return it->second; 342 } 343 344 // WatchDogThread methods and members. 345 // 346 // static 347 base::Lock WatchDogThread::lock_; 348 // static 349 WatchDogThread* WatchDogThread::watchdog_thread_ = NULL; 350 351 // The WatchDogThread object must outlive any tasks posted to the IO thread 352 // before the Quit task. 353 DISABLE_RUNNABLE_METHOD_REFCOUNT(WatchDogThread); 354 355 WatchDogThread::WatchDogThread() : Thread("WATCHDOG") { 356 } 357 358 WatchDogThread::~WatchDogThread() { 359 // We cannot rely on our base class to stop the thread since we want our 360 // CleanUp function to run. 361 Stop(); 362 } 363 364 // static 365 bool WatchDogThread::CurrentlyOnWatchDogThread() { 366 base::AutoLock lock(lock_); 367 return watchdog_thread_ && 368 watchdog_thread_->message_loop() == MessageLoop::current(); 369 } 370 371 // static 372 bool WatchDogThread::PostTask(const tracked_objects::Location& from_here, 373 Task* task) { 374 return PostTaskHelper(from_here, task, 0); 375 } 376 377 // static 378 bool WatchDogThread::PostDelayedTask(const tracked_objects::Location& from_here, 379 Task* task, 380 int64 delay_ms) { 381 return PostTaskHelper(from_here, task, delay_ms); 382 } 383 384 // static 385 bool WatchDogThread::PostTaskHelper( 386 const tracked_objects::Location& from_here, 387 Task* task, 388 int64 delay_ms) { 389 { 390 base::AutoLock lock(lock_); 391 392 MessageLoop* message_loop = watchdog_thread_ ? 393 watchdog_thread_->message_loop() : NULL; 394 if (message_loop) { 395 message_loop->PostDelayedTask(from_here, task, delay_ms); 396 return true; 397 } 398 } 399 delete task; 400 401 return false; 402 } 403 404 void WatchDogThread::Init() { 405 // This thread shouldn't be allowed to perform any blocking disk I/O. 406 base::ThreadRestrictions::SetIOAllowed(false); 407 408 #if defined(OS_WIN) 409 // Initializes the COM library on the current thread. 410 HRESULT result = CoInitialize(NULL); 411 CHECK(result == S_OK); 412 #endif 413 414 base::AutoLock lock(lock_); 415 CHECK(!watchdog_thread_); 416 watchdog_thread_ = this; 417 } 418 419 void WatchDogThread::CleanUp() { 420 base::AutoLock lock(lock_); 421 watchdog_thread_ = NULL; 422 } 423 424 void WatchDogThread::CleanUpAfterMessageLoopDestruction() { 425 #if defined(OS_WIN) 426 // Closes the COM library on the current thread. CoInitialize must 427 // be balanced by a corresponding call to CoUninitialize. 428 CoUninitialize(); 429 #endif 430 } 431