• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
ThreadWatcher(const BrowserThread::ID & thread_id,const std::string & thread_name,const base::TimeDelta & sleep_time,const base::TimeDelta & unresponsive_time)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 
~ThreadWatcher()36 ThreadWatcher::~ThreadWatcher() {}
37 
38 // static
StartWatching(const BrowserThread::ID & thread_id,const std::string & thread_name,const base::TimeDelta & sleep_time,const base::TimeDelta & unresponsive_time)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 
ActivateThreadWatching()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 
DeActivateThreadWatching()80 void ThreadWatcher::DeActivateThreadWatching() {
81   DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
82   active_ = false;
83   ping_count_ = 0;
84   method_factory_.RevokeAll();
85 }
86 
WakeUp()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 
PostPingMessage()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 
OnPongMessage(uint64 ping_sequence_number)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 
OnCheckResponsiveness(uint64 ping_sequence_number)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 
Initialize()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
OnPingMessage(const BrowserThread::ID & thread_id,Task * callback_task)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 
ThreadWatcherList()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(&registrar_, this);
202 }
203 
~ThreadWatcherList()204 ThreadWatcherList::~ThreadWatcherList() {
205   base::AutoLock auto_lock(lock_);
206   DCHECK(this == global_);
207   global_ = NULL;
208 }
209 
210 // static
Register(ThreadWatcher * watcher)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
IsRegistered(const BrowserThread::ID thread_id)220 bool ThreadWatcherList::IsRegistered(const BrowserThread::ID thread_id) {
221   return NULL != ThreadWatcherList::Find(thread_id);
222 }
223 
224 // static
StartWatchingAll()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
StopWatchingAll()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
RemoveNotifications()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 
DeleteAll()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 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)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 
WakeUpAll()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
Find(const BrowserThread::ID & thread_id)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 
PreLockedFind(const BrowserThread::ID & thread_id)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 
WatchDogThread()355 WatchDogThread::WatchDogThread() : Thread("WATCHDOG") {
356 }
357 
~WatchDogThread()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
CurrentlyOnWatchDogThread()365 bool WatchDogThread::CurrentlyOnWatchDogThread() {
366   base::AutoLock lock(lock_);
367   return watchdog_thread_ &&
368     watchdog_thread_->message_loop() == MessageLoop::current();
369 }
370 
371 // static
PostTask(const tracked_objects::Location & from_here,Task * task)372 bool WatchDogThread::PostTask(const tracked_objects::Location& from_here,
373                               Task* task) {
374   return PostTaskHelper(from_here, task, 0);
375 }
376 
377 // static
PostDelayedTask(const tracked_objects::Location & from_here,Task * task,int64 delay_ms)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
PostTaskHelper(const tracked_objects::Location & from_here,Task * task,int64 delay_ms)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 
Init()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 
CleanUp()419 void WatchDogThread::CleanUp() {
420   base::AutoLock lock(lock_);
421   watchdog_thread_ = NULL;
422 }
423 
CleanUpAfterMessageLoopDestruction()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