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
12namespace base {
13
14namespace {
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
23struct 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
34LazyInstance<StaticData>::Leaky g_static_data = LAZY_INSTANCE_INITIALIZER;
35
36}  // namespace
37
38// Start thread running in a Disarmed state.
39Watchdog::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.
58Watchdog::~Watchdog() {
59  if (!enabled_)
60    return;
61  if (!IsJoinable())
62    Cleanup();
63  condition_variable_.Signal();
64  PlatformThread::Join(handle_);
65}
66
67void Watchdog::Cleanup() {
68  if (!enabled_)
69    return;
70  {
71    AutoLock lock(lock_);
72    state_ = SHUTDOWN;
73  }
74  condition_variable_.Signal();
75}
76
77bool Watchdog::IsJoinable() {
78  if (!enabled_)
79    return true;
80  AutoLock lock(lock_);
81  return (state_ == JOINABLE);
82}
83
84void Watchdog::Arm() {
85  ArmAtStartTime(TimeTicks::Now());
86}
87
88void Watchdog::ArmSomeTimeDeltaAgo(const TimeDelta& time_delta) {
89  ArmAtStartTime(TimeTicks::Now() - time_delta);
90}
91
92// Start clock for watchdog.
93void 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.
105void 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
112void Watchdog::Alarm() {
113  DVLOG(1) << "Watchdog alarmed for " << thread_watched_name_;
114}
115
116//------------------------------------------------------------------------------
117// Internal private methods that the watchdog thread uses.
118
119void 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
170void 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
177void 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