Home | History | Annotate | Download | only in threading
      1 // Copyright (c) 2012 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/watchdog.h"
      6 
      7 #include "base/compiler_specific.h"
      8 #include "base/lazy_instance.h"
      9 #include "base/logging.h"
     10 #include "base/threading/platform_thread.h"
     11 
     12 namespace base {
     13 
     14 namespace {
     15 
     16 // When the debugger breaks (when we alarm), all the other alarms that are
     17 // armed will expire (also alarm).  To diminish this effect, we track any
     18 // delay due to debugger breaks, and we *try* to adjust the effective start
     19 // time of other alarms to step past the debugging break.
     20 // Without this safety net, any alarm will typically trigger a host of follow
     21 // on alarms from callers that specify old times.
     22 
     23 // Lock for access of static data...
     24 LazyInstance<Lock>::Leaky g_static_lock = LAZY_INSTANCE_INITIALIZER;
     25 
     26 // When did we last alarm and get stuck (for a while) in a debugger?
     27 TimeTicks g_last_debugged_alarm_time;
     28 
     29 // How long did we sit on a break in the debugger?
     30 TimeDelta g_last_debugged_alarm_delay;
     31 
     32 }  // namespace
     33 
     34 // Start thread running in a Disarmed state.
     35 Watchdog::Watchdog(const TimeDelta& duration,
     36                    const std::string& thread_watched_name,
     37                    bool enabled)
     38   : enabled_(enabled),
     39     lock_(),
     40     condition_variable_(&lock_),
     41     state_(DISARMED),
     42     duration_(duration),
     43     thread_watched_name_(thread_watched_name),
     44     delegate_(this) {
     45   if (!enabled_)
     46     return;  // Don't start thread, or doing anything really.
     47   enabled_ = PlatformThread::Create(0,  // Default stack size.
     48                                     &delegate_,
     49                                     &handle_);
     50   DCHECK(enabled_);
     51 }
     52 
     53 // Notify watchdog thread, and wait for it to finish up.
     54 Watchdog::~Watchdog() {
     55   if (!enabled_)
     56     return;
     57   if (!IsJoinable())
     58     Cleanup();
     59   condition_variable_.Signal();
     60   PlatformThread::Join(handle_);
     61 }
     62 
     63 void Watchdog::Cleanup() {
     64   if (!enabled_)
     65     return;
     66   {
     67     AutoLock lock(lock_);
     68     state_ = SHUTDOWN;
     69   }
     70   condition_variable_.Signal();
     71 }
     72 
     73 bool Watchdog::IsJoinable() {
     74   if (!enabled_)
     75     return true;
     76   AutoLock lock(lock_);
     77   return (state_ == JOINABLE);
     78 }
     79 
     80 void Watchdog::Arm() {
     81   ArmAtStartTime(TimeTicks::Now());
     82 }
     83 
     84 void Watchdog::ArmSomeTimeDeltaAgo(const TimeDelta& time_delta) {
     85   ArmAtStartTime(TimeTicks::Now() - time_delta);
     86 }
     87 
     88 // Start clock for watchdog.
     89 void Watchdog::ArmAtStartTime(const TimeTicks start_time) {
     90   {
     91     AutoLock lock(lock_);
     92     start_time_ = start_time;
     93     state_ = ARMED;
     94   }
     95   // Force watchdog to wake up, and go to sleep with the timer ticking with the
     96   // proper duration.
     97   condition_variable_.Signal();
     98 }
     99 
    100 // Disable watchdog so that it won't do anything when time expires.
    101 void Watchdog::Disarm() {
    102   AutoLock lock(lock_);
    103   state_ = DISARMED;
    104   // We don't need to signal, as the watchdog will eventually wake up, and it
    105   // will check its state and time, and act accordingly.
    106 }
    107 
    108 void Watchdog::Alarm() {
    109   DVLOG(1) << "Watchdog alarmed for " << thread_watched_name_;
    110 }
    111 
    112 //------------------------------------------------------------------------------
    113 // Internal private methods that the watchdog thread uses.
    114 
    115 void Watchdog::ThreadDelegate::ThreadMain() {
    116   SetThreadName();
    117   TimeDelta remaining_duration;
    118   while (1) {
    119     AutoLock lock(watchdog_->lock_);
    120     while (DISARMED == watchdog_->state_)
    121       watchdog_->condition_variable_.Wait();
    122     if (SHUTDOWN == watchdog_->state_) {
    123       watchdog_->state_ = JOINABLE;
    124       return;
    125     }
    126     DCHECK(ARMED == watchdog_->state_);
    127     remaining_duration = watchdog_->duration_ -
    128         (TimeTicks::Now() - watchdog_->start_time_);
    129     if (remaining_duration.InMilliseconds() > 0) {
    130       // Spurios wake?  Timer drifts?  Go back to sleep for remaining time.
    131       watchdog_->condition_variable_.TimedWait(remaining_duration);
    132       continue;
    133     }
    134     // We overslept, so this seems like a real alarm.
    135     // Watch out for a user that stopped the debugger on a different alarm!
    136     {
    137       AutoLock static_lock(*g_static_lock.Pointer());
    138       if (g_last_debugged_alarm_time > watchdog_->start_time_) {
    139         // False alarm: we started our clock before the debugger break (last
    140         // alarm time).
    141         watchdog_->start_time_ += g_last_debugged_alarm_delay;
    142         if (g_last_debugged_alarm_time > watchdog_->start_time_)
    143           // Too many alarms must have taken place.
    144           watchdog_->state_ = DISARMED;
    145         continue;
    146       }
    147     }
    148     watchdog_->state_ = DISARMED;  // Only alarm at most once.
    149     TimeTicks last_alarm_time = TimeTicks::Now();
    150     {
    151       AutoUnlock lock(watchdog_->lock_);
    152       watchdog_->Alarm();  // Set a break point here to debug on alarms.
    153     }
    154     TimeDelta last_alarm_delay = TimeTicks::Now() - last_alarm_time;
    155     if (last_alarm_delay <= TimeDelta::FromMilliseconds(2))
    156       continue;
    157     // Ignore race of two alarms/breaks going off at roughly the same time.
    158     AutoLock static_lock(*g_static_lock.Pointer());
    159     // This was a real debugger break.
    160     g_last_debugged_alarm_time = last_alarm_time;
    161     g_last_debugged_alarm_delay = last_alarm_delay;
    162   }
    163 }
    164 
    165 void Watchdog::ThreadDelegate::SetThreadName() const {
    166   std::string name = watchdog_->thread_watched_name_ + " Watchdog";
    167   PlatformThread::SetName(name.c_str());
    168   DVLOG(1) << "Watchdog active: " << name;
    169 }
    170 
    171 // static
    172 void Watchdog::ResetStaticData() {
    173   AutoLock lock(*g_static_lock.Pointer());
    174   g_last_debugged_alarm_time = TimeTicks();
    175   g_last_debugged_alarm_delay = TimeDelta();
    176 }
    177 
    178 }  // namespace base
    179