blob: 96dd9b943e594c371e14960890c73f0345c7218a [file] [log] [blame]
license.botf003cfe2008-08-24 09:55:55 +09001// Copyright (c) 2006-2008 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.
initial.commit3f4a7322008-07-27 06:49:38 +09004
5#include "base/watchdog.h"
darin@google.comc18d7ae2008-08-21 18:46:32 +09006
7#include "base/platform_thread.h"
initial.commit3f4a7322008-07-27 06:49:38 +09008#include "base/string_util.h"
initial.commit3f4a7322008-07-27 06:49:38 +09009
10//------------------------------------------------------------------------------
11// Public API methods.
12
13// Start thread running in a Disarmed state.
14Watchdog::Watchdog(const TimeDelta& duration,
15 const std::wstring& thread_watched_name,
16 bool enabled)
17 : lock_(),
18 condition_variable_(&lock_),
19 state_(DISARMED),
20 duration_(duration),
21 thread_watched_name_(thread_watched_name),
22 handle_(NULL),
23 thread_id_(0) {
24 if (!enabled)
25 return; // Don't start thread, or doing anything really.
26 handle_ = CreateThread(NULL, // security
27 0, // Default stack size.
28 Watchdog::ThreadStart,
29 reinterpret_cast<void*>(this),
30 CREATE_SUSPENDED,
31 &thread_id_);
32 DCHECK(NULL != handle_);
33 if (NULL == handle_)
34 return ;
35 ResumeThread(handle_); // WINAPI call.
36}
37
38// Notify watchdog thread, and wait for it to finish up.
39Watchdog::~Watchdog() {
40 if (NULL == handle_)
41 return;
42 {
43 AutoLock lock(lock_);
44 state_ = SHUTDOWN;
45 }
46 condition_variable_.Signal();
47 DWORD results = WaitForSingleObject(handle_, INFINITE);
48 DCHECK(WAIT_OBJECT_0 == results);
49 CloseHandle(handle_);
50 handle_ = NULL;
51}
52
53void Watchdog::Arm() {
54 ArmAtStartTime(TimeTicks::Now());
55}
56
57void Watchdog::ArmSomeTimeDeltaAgo(const TimeDelta& time_delta) {
58 ArmAtStartTime(TimeTicks::Now() - time_delta);
59}
60
61// Start clock for watchdog.
62void Watchdog::ArmAtStartTime(const TimeTicks start_time) {
63 {
64 AutoLock lock(lock_);
65 start_time_ = start_time;
66 state_ = ARMED;
67 }
68 // Force watchdog to wake up, and go to sleep with the timer ticking with the
69 // proper duration.
70 condition_variable_.Signal();
71}
72
73// Disable watchdog so that it won't do anything when time expires.
74void Watchdog::Disarm() {
75 if (NULL == handle_)
76 return;
77 AutoLock lock(lock_);
78 state_ = DISARMED;
79 // We don't need to signal, as the watchdog will eventually wake up, and it
80 // will check its state and time, and act accordingly.
81}
82
83//------------------------------------------------------------------------------
84// Internal private methods that the watchdog thread uses.
85
86// static
87DWORD __stdcall Watchdog::ThreadStart(void* pThis) {
88 Watchdog* watchdog = reinterpret_cast<Watchdog*>(pThis);
89 return watchdog->Run();
90}
91
92unsigned Watchdog::Run() {
93 SetThreadName();
94 TimeDelta remaining_duration;
95 while (1) {
96 AutoLock lock(lock_);
97 while (DISARMED == state_)
98 condition_variable_.Wait();
99 if (SHUTDOWN == state_)
100 return 0;
101 DCHECK(ARMED == state_);
102 remaining_duration = duration_ - (TimeTicks::Now() - start_time_);
103 if (remaining_duration.InMilliseconds() > 0) {
104 // Spurios wake? Timer drifts? Go back to sleep for remaining time.
105 condition_variable_.TimedWait(remaining_duration);
106 } else {
107 // We overslept, so this seems like a real alarm.
108 // Watch out for a user that stopped the debugger on a different alarm!
109 {
110 AutoLock static_lock(static_lock_);
111 if (last_debugged_alarm_time_ > start_time_) {
112 // False alarm: we started our clock before the debugger break (last
113 // alarm time).
114 start_time_ += last_debugged_alarm_delay_;
115 if (last_debugged_alarm_time_ > start_time_)
116 state_ = DISARMED; // Too many alarms must have taken place.
117 continue;
118 }
119 }
120 state_ = DISARMED; // Only alarm at most once.
121 TimeTicks last_alarm_time = TimeTicks::Now();
122 Alarm(); // Set a break point here to debug on alarms.
123 TimeDelta last_alarm_delay = TimeTicks::Now() - last_alarm_time;
124 if (last_alarm_delay > TimeDelta::FromMilliseconds(2)) {
125 // Ignore race of two alarms/breaks going off at roughly the same time.
126 AutoLock static_lock(static_lock_);
127 // This was a real debugger break.
128 last_debugged_alarm_time_ = last_alarm_time;
129 last_debugged_alarm_delay_ = last_alarm_delay;
130 }
131 }
132 }
133}
134
135void Watchdog::SetThreadName() const {
136 std::string name = StringPrintf("%s Watchdog",
137 WideToASCII(thread_watched_name_).c_str());
darin@google.comc18d7ae2008-08-21 18:46:32 +0900138 PlatformThread::SetName(thread_id_, name.c_str());
initial.commit3f4a7322008-07-27 06:49:38 +0900139 DLOG(INFO) << "Watchdog active: " << name;
140}
141
142// static
143Lock Watchdog::static_lock_; // Lock for access of static data...
144// static
145TimeTicks Watchdog::last_debugged_alarm_time_ = TimeTicks();
146// static
147TimeDelta Watchdog::last_debugged_alarm_delay_;
license.botf003cfe2008-08-24 09:55:55 +0900148