diff options
Diffstat (limited to 'base/watchdog.cc')
-rw-r--r-- | base/watchdog.cc | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/base/watchdog.cc b/base/watchdog.cc new file mode 100644 index 0000000..1d9c183 --- /dev/null +++ b/base/watchdog.cc @@ -0,0 +1,171 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "base/watchdog.h" +#include "base/string_util.h" +#include "base/thread.h" + +//------------------------------------------------------------------------------ +// Public API methods. + +// Start thread running in a Disarmed state. +Watchdog::Watchdog(const TimeDelta& duration, + const std::wstring& thread_watched_name, + bool enabled) + : lock_(), + condition_variable_(&lock_), + state_(DISARMED), + duration_(duration), + thread_watched_name_(thread_watched_name), + handle_(NULL), + thread_id_(0) { + if (!enabled) + return; // Don't start thread, or doing anything really. + handle_ = CreateThread(NULL, // security + 0, // Default stack size. + Watchdog::ThreadStart, + reinterpret_cast<void*>(this), + CREATE_SUSPENDED, + &thread_id_); + DCHECK(NULL != handle_); + if (NULL == handle_) + return ; + ResumeThread(handle_); // WINAPI call. +} + +// Notify watchdog thread, and wait for it to finish up. +Watchdog::~Watchdog() { + if (NULL == handle_) + return; + { + AutoLock lock(lock_); + state_ = SHUTDOWN; + } + condition_variable_.Signal(); + DWORD results = WaitForSingleObject(handle_, INFINITE); + DCHECK(WAIT_OBJECT_0 == results); + CloseHandle(handle_); + handle_ = NULL; +} + +void Watchdog::Arm() { + ArmAtStartTime(TimeTicks::Now()); +} + +void Watchdog::ArmSomeTimeDeltaAgo(const TimeDelta& time_delta) { + ArmAtStartTime(TimeTicks::Now() - time_delta); +} + +// Start clock for watchdog. +void Watchdog::ArmAtStartTime(const TimeTicks start_time) { + { + AutoLock lock(lock_); + start_time_ = start_time; + state_ = ARMED; + } + // Force watchdog to wake up, and go to sleep with the timer ticking with the + // proper duration. + condition_variable_.Signal(); +} + +// Disable watchdog so that it won't do anything when time expires. +void Watchdog::Disarm() { + if (NULL == handle_) + return; + AutoLock lock(lock_); + state_ = DISARMED; + // We don't need to signal, as the watchdog will eventually wake up, and it + // will check its state and time, and act accordingly. +} + +//------------------------------------------------------------------------------ +// Internal private methods that the watchdog thread uses. + +// static +DWORD __stdcall Watchdog::ThreadStart(void* pThis) { + Watchdog* watchdog = reinterpret_cast<Watchdog*>(pThis); + return watchdog->Run(); +} + +unsigned Watchdog::Run() { + SetThreadName(); + TimeDelta remaining_duration; + while (1) { + AutoLock lock(lock_); + while (DISARMED == state_) + condition_variable_.Wait(); + if (SHUTDOWN == state_) + return 0; + DCHECK(ARMED == state_); + remaining_duration = duration_ - (TimeTicks::Now() - start_time_); + if (remaining_duration.InMilliseconds() > 0) { + // Spurios wake? Timer drifts? Go back to sleep for remaining time. + condition_variable_.TimedWait(remaining_duration); + } else { + // We overslept, so this seems like a real alarm. + // Watch out for a user that stopped the debugger on a different alarm! + { + AutoLock static_lock(static_lock_); + if (last_debugged_alarm_time_ > start_time_) { + // False alarm: we started our clock before the debugger break (last + // alarm time). + start_time_ += last_debugged_alarm_delay_; + if (last_debugged_alarm_time_ > start_time_) + state_ = DISARMED; // Too many alarms must have taken place. + continue; + } + } + state_ = DISARMED; // Only alarm at most once. + TimeTicks last_alarm_time = TimeTicks::Now(); + Alarm(); // Set a break point here to debug on alarms. + TimeDelta last_alarm_delay = TimeTicks::Now() - last_alarm_time; + if (last_alarm_delay > TimeDelta::FromMilliseconds(2)) { + // Ignore race of two alarms/breaks going off at roughly the same time. + AutoLock static_lock(static_lock_); + // This was a real debugger break. + last_debugged_alarm_time_ = last_alarm_time; + last_debugged_alarm_delay_ = last_alarm_delay; + } + } + } +} + +void Watchdog::SetThreadName() const { + std::string name = StringPrintf("%s Watchdog", + WideToASCII(thread_watched_name_).c_str()); + Thread::SetThreadName(name.c_str(), thread_id_); + DLOG(INFO) << "Watchdog active: " << name; +} + +// static +Lock Watchdog::static_lock_; // Lock for access of static data... +// static +TimeTicks Watchdog::last_debugged_alarm_time_ = TimeTicks(); +// static +TimeDelta Watchdog::last_debugged_alarm_delay_; |