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