blob: 1d9c183ae709e528e82c55284571bb41bd9d164e [file] [log] [blame]
initial.commit3f4a7322008-07-27 06:49:38 +09001// Copyright 2008, Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14// * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30#include "base/watchdog.h"
31#include "base/string_util.h"
32#include "base/thread.h"
33
34//------------------------------------------------------------------------------
35// Public API methods.
36
37// Start thread running in a Disarmed state.
38Watchdog::Watchdog(const TimeDelta& duration,
39 const std::wstring& thread_watched_name,
40 bool enabled)
41 : lock_(),
42 condition_variable_(&lock_),
43 state_(DISARMED),
44 duration_(duration),
45 thread_watched_name_(thread_watched_name),
46 handle_(NULL),
47 thread_id_(0) {
48 if (!enabled)
49 return; // Don't start thread, or doing anything really.
50 handle_ = CreateThread(NULL, // security
51 0, // Default stack size.
52 Watchdog::ThreadStart,
53 reinterpret_cast<void*>(this),
54 CREATE_SUSPENDED,
55 &thread_id_);
56 DCHECK(NULL != handle_);
57 if (NULL == handle_)
58 return ;
59 ResumeThread(handle_); // WINAPI call.
60}
61
62// Notify watchdog thread, and wait for it to finish up.
63Watchdog::~Watchdog() {
64 if (NULL == handle_)
65 return;
66 {
67 AutoLock lock(lock_);
68 state_ = SHUTDOWN;
69 }
70 condition_variable_.Signal();
71 DWORD results = WaitForSingleObject(handle_, INFINITE);
72 DCHECK(WAIT_OBJECT_0 == results);
73 CloseHandle(handle_);
74 handle_ = NULL;
75}
76
77void Watchdog::Arm() {
78 ArmAtStartTime(TimeTicks::Now());
79}
80
81void Watchdog::ArmSomeTimeDeltaAgo(const TimeDelta& time_delta) {
82 ArmAtStartTime(TimeTicks::Now() - time_delta);
83}
84
85// Start clock for watchdog.
86void Watchdog::ArmAtStartTime(const TimeTicks start_time) {
87 {
88 AutoLock lock(lock_);
89 start_time_ = start_time;
90 state_ = ARMED;
91 }
92 // Force watchdog to wake up, and go to sleep with the timer ticking with the
93 // proper duration.
94 condition_variable_.Signal();
95}
96
97// Disable watchdog so that it won't do anything when time expires.
98void Watchdog::Disarm() {
99 if (NULL == handle_)
100 return;
101 AutoLock lock(lock_);
102 state_ = DISARMED;
103 // We don't need to signal, as the watchdog will eventually wake up, and it
104 // will check its state and time, and act accordingly.
105}
106
107//------------------------------------------------------------------------------
108// Internal private methods that the watchdog thread uses.
109
110// static
111DWORD __stdcall Watchdog::ThreadStart(void* pThis) {
112 Watchdog* watchdog = reinterpret_cast<Watchdog*>(pThis);
113 return watchdog->Run();
114}
115
116unsigned Watchdog::Run() {
117 SetThreadName();
118 TimeDelta remaining_duration;
119 while (1) {
120 AutoLock lock(lock_);
121 while (DISARMED == state_)
122 condition_variable_.Wait();
123 if (SHUTDOWN == state_)
124 return 0;
125 DCHECK(ARMED == state_);
126 remaining_duration = duration_ - (TimeTicks::Now() - start_time_);
127 if (remaining_duration.InMilliseconds() > 0) {
128 // Spurios wake? Timer drifts? Go back to sleep for remaining time.
129 condition_variable_.TimedWait(remaining_duration);
130 } else {
131 // We overslept, so this seems like a real alarm.
132 // Watch out for a user that stopped the debugger on a different alarm!
133 {
134 AutoLock static_lock(static_lock_);
135 if (last_debugged_alarm_time_ > start_time_) {
136 // False alarm: we started our clock before the debugger break (last
137 // alarm time).
138 start_time_ += last_debugged_alarm_delay_;
139 if (last_debugged_alarm_time_ > start_time_)
140 state_ = DISARMED; // Too many alarms must have taken place.
141 continue;
142 }
143 }
144 state_ = DISARMED; // Only alarm at most once.
145 TimeTicks last_alarm_time = TimeTicks::Now();
146 Alarm(); // Set a break point here to debug on alarms.
147 TimeDelta last_alarm_delay = TimeTicks::Now() - last_alarm_time;
148 if (last_alarm_delay > TimeDelta::FromMilliseconds(2)) {
149 // Ignore race of two alarms/breaks going off at roughly the same time.
150 AutoLock static_lock(static_lock_);
151 // This was a real debugger break.
152 last_debugged_alarm_time_ = last_alarm_time;
153 last_debugged_alarm_delay_ = last_alarm_delay;
154 }
155 }
156 }
157}
158
159void Watchdog::SetThreadName() const {
160 std::string name = StringPrintf("%s Watchdog",
161 WideToASCII(thread_watched_name_).c_str());
162 Thread::SetThreadName(name.c_str(), thread_id_);
163 DLOG(INFO) << "Watchdog active: " << name;
164}
165
166// static
167Lock Watchdog::static_lock_; // Lock for access of static data...
168// static
169TimeTicks Watchdog::last_debugged_alarm_time_ = TimeTicks();
170// static
171TimeDelta Watchdog::last_debugged_alarm_delay_;