Home | History | Annotate | Download | only in threading
      1 // Copyright (c) 2010 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/logging.h"
      9 #include "base/threading/platform_thread.h"
     10 
     11 namespace base {
     12 
     13 // Start thread running in a Disarmed state.
     14 Watchdog::Watchdog(const TimeDelta& duration,
     15                    const std::string& thread_watched_name,
     16                    bool enabled)
     17   : init_successful_(false),
     18     lock_(),
     19     condition_variable_(&lock_),
     20     state_(DISARMED),
     21     duration_(duration),
     22     thread_watched_name_(thread_watched_name),
     23     ALLOW_THIS_IN_INITIALIZER_LIST(delegate_(this)) {
     24   if (!enabled)
     25     return;  // Don't start thread, or doing anything really.
     26   init_successful_ = PlatformThread::Create(0,  // Default stack size.
     27                                             &delegate_,
     28                                             &handle_);
     29   DCHECK(init_successful_);
     30 }
     31 
     32 // Notify watchdog thread, and wait for it to finish up.
     33 Watchdog::~Watchdog() {
     34   if (!init_successful_)
     35     return;
     36   {
     37     AutoLock lock(lock_);
     38     state_ = SHUTDOWN;
     39   }
     40   condition_variable_.Signal();
     41   PlatformThread::Join(handle_);
     42 }
     43 
     44 void Watchdog::Arm() {
     45   ArmAtStartTime(TimeTicks::Now());
     46 }
     47 
     48 void Watchdog::ArmSomeTimeDeltaAgo(const TimeDelta& time_delta) {
     49   ArmAtStartTime(TimeTicks::Now() - time_delta);
     50 }
     51 
     52 // Start clock for watchdog.
     53 void Watchdog::ArmAtStartTime(const TimeTicks start_time) {
     54   {
     55     AutoLock lock(lock_);
     56     start_time_ = start_time;
     57     state_ = ARMED;
     58   }
     59   // Force watchdog to wake up, and go to sleep with the timer ticking with the
     60   // proper duration.
     61   condition_variable_.Signal();
     62 }
     63 
     64 // Disable watchdog so that it won't do anything when time expires.
     65 void Watchdog::Disarm() {
     66   AutoLock lock(lock_);
     67   state_ = DISARMED;
     68   // We don't need to signal, as the watchdog will eventually wake up, and it
     69   // will check its state and time, and act accordingly.
     70 }
     71 
     72 void Watchdog::Alarm() {
     73   DVLOG(1) << "Watchdog alarmed for " << thread_watched_name_;
     74 }
     75 
     76 //------------------------------------------------------------------------------
     77 // Internal private methods that the watchdog thread uses.
     78 
     79 void Watchdog::ThreadDelegate::ThreadMain() {
     80   SetThreadName();
     81   TimeDelta remaining_duration;
     82   while (1) {
     83     AutoLock lock(watchdog_->lock_);
     84     while (DISARMED == watchdog_->state_)
     85       watchdog_->condition_variable_.Wait();
     86     if (SHUTDOWN == watchdog_->state_)
     87       return;
     88     DCHECK(ARMED == watchdog_->state_);
     89     remaining_duration = watchdog_->duration_ -
     90         (TimeTicks::Now() - watchdog_->start_time_);
     91     if (remaining_duration.InMilliseconds() > 0) {
     92       // Spurios wake?  Timer drifts?  Go back to sleep for remaining time.
     93       watchdog_->condition_variable_.TimedWait(remaining_duration);
     94       continue;
     95     }
     96     // We overslept, so this seems like a real alarm.
     97     // Watch out for a user that stopped the debugger on a different alarm!
     98     {
     99       AutoLock static_lock(static_lock_);
    100       if (last_debugged_alarm_time_ > watchdog_->start_time_) {
    101         // False alarm: we started our clock before the debugger break (last
    102         // alarm time).
    103         watchdog_->start_time_ += last_debugged_alarm_delay_;
    104         if (last_debugged_alarm_time_ > watchdog_->start_time_)
    105           // Too many alarms must have taken place.
    106           watchdog_->state_ = DISARMED;
    107         continue;
    108       }
    109     }
    110     watchdog_->state_ = DISARMED;  // Only alarm at most once.
    111     TimeTicks last_alarm_time = TimeTicks::Now();
    112     watchdog_->Alarm();  // Set a break point here to debug on alarms.
    113     TimeDelta last_alarm_delay = TimeTicks::Now() - last_alarm_time;
    114     if (last_alarm_delay <= TimeDelta::FromMilliseconds(2))
    115       continue;
    116     // Ignore race of two alarms/breaks going off at roughly the same time.
    117     AutoLock static_lock(static_lock_);
    118     // This was a real debugger break.
    119     last_debugged_alarm_time_ = last_alarm_time;
    120     last_debugged_alarm_delay_ = last_alarm_delay;
    121   }
    122 }
    123 
    124 void Watchdog::ThreadDelegate::SetThreadName() const {
    125   std::string name = watchdog_->thread_watched_name_ + " Watchdog";
    126   PlatformThread::SetName(name.c_str());
    127   DVLOG(1) << "Watchdog active: " << name;
    128 }
    129 
    130 // static
    131 void Watchdog::ResetStaticData() {
    132   AutoLock lock(static_lock_);
    133   last_debugged_alarm_time_ = TimeTicks();
    134   last_debugged_alarm_delay_ = TimeDelta();
    135 }
    136 
    137 // static
    138 Lock Watchdog::static_lock_;  // Lock for access of static data...
    139 // static
    140 TimeTicks Watchdog::last_debugged_alarm_time_ = TimeTicks();
    141 // static
    142 TimeDelta Watchdog::last_debugged_alarm_delay_;
    143 
    144 }  // namespace base
    145