watchdog.cc revision 3f50c38dc070f4bb515c1b64450dae14f316474e
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
11namespace base {
12
13// Start thread running in a Disarmed state.
14Watchdog::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.
33Watchdog::~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
44void Watchdog::Arm() {
45  ArmAtStartTime(TimeTicks::Now());
46}
47
48void Watchdog::ArmSomeTimeDeltaAgo(const TimeDelta& time_delta) {
49  ArmAtStartTime(TimeTicks::Now() - time_delta);
50}
51
52// Start clock for watchdog.
53void 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.
65void 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
72void Watchdog::Alarm() {
73  DVLOG(1) << "Watchdog alarmed for " << thread_watched_name_;
74}
75
76//------------------------------------------------------------------------------
77// Internal private methods that the watchdog thread uses.
78
79void 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
124void 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
131void Watchdog::ResetStaticData() {
132  AutoLock lock(static_lock_);
133  last_debugged_alarm_time_ = TimeTicks();
134  last_debugged_alarm_delay_ = TimeDelta();
135}
136
137// static
138Lock Watchdog::static_lock_;  // Lock for access of static data...
139// static
140TimeTicks Watchdog::last_debugged_alarm_time_ = TimeTicks();
141// static
142TimeDelta Watchdog::last_debugged_alarm_delay_;
143
144}  // namespace base
145