|  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "base/threading/watchdog.h" | 
|  |  | 
|  | #include "base/compiler_specific.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/threading/platform_thread.h" | 
|  |  | 
|  | namespace base { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // When the debugger breaks (when we alarm), all the other alarms that are | 
|  | // armed will expire (also alarm).  To diminish this effect, we track any | 
|  | // delay due to debugger breaks, and we *try* to adjust the effective start | 
|  | // time of other alarms to step past the debugging break. | 
|  | // Without this safety net, any alarm will typically trigger a host of follow | 
|  | // on alarms from callers that specify old times. | 
|  |  | 
|  | struct StaticData { | 
|  | // Lock for access of static data... | 
|  | Lock lock; | 
|  |  | 
|  | // When did we last alarm and get stuck (for a while) in a debugger? | 
|  | TimeTicks last_debugged_alarm_time; | 
|  |  | 
|  | // How long did we sit on a break in the debugger? | 
|  | TimeDelta last_debugged_alarm_delay; | 
|  | }; | 
|  |  | 
|  | StaticData* GetStaticData() { | 
|  | static auto* static_data = new StaticData(); | 
|  | return static_data; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // Start thread running in a Disarmed state. | 
|  | Watchdog::Watchdog(const TimeDelta& duration, | 
|  | const std::string& thread_watched_name, | 
|  | bool enabled) | 
|  | : enabled_(enabled), | 
|  | lock_(), | 
|  | condition_variable_(&lock_), | 
|  | state_(DISARMED), | 
|  | duration_(duration), | 
|  | thread_watched_name_(thread_watched_name), | 
|  | delegate_(this) { | 
|  | if (!enabled_) | 
|  | return;  // Don't start thread, or doing anything really. | 
|  | enabled_ = PlatformThread::Create(0,  // Default stack size. | 
|  | &delegate_, | 
|  | &handle_); | 
|  | DCHECK(enabled_); | 
|  | } | 
|  |  | 
|  | // Notify watchdog thread, and wait for it to finish up. | 
|  | Watchdog::~Watchdog() { | 
|  | if (!enabled_) | 
|  | return; | 
|  | if (!IsJoinable()) | 
|  | Cleanup(); | 
|  | condition_variable_.Signal(); | 
|  | PlatformThread::Join(handle_); | 
|  | } | 
|  |  | 
|  | void Watchdog::Cleanup() { | 
|  | if (!enabled_) | 
|  | return; | 
|  | { | 
|  | AutoLock lock(lock_); | 
|  | state_ = SHUTDOWN; | 
|  | } | 
|  | condition_variable_.Signal(); | 
|  | } | 
|  |  | 
|  | bool Watchdog::IsJoinable() { | 
|  | if (!enabled_) | 
|  | return true; | 
|  | AutoLock lock(lock_); | 
|  | return (state_ == JOINABLE); | 
|  | } | 
|  |  | 
|  | 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() { | 
|  | 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. | 
|  | } | 
|  |  | 
|  | void Watchdog::Alarm() { | 
|  | DVLOG(1) << "Watchdog alarmed for " << thread_watched_name_; | 
|  | } | 
|  |  | 
|  | //------------------------------------------------------------------------------ | 
|  | // Internal private methods that the watchdog thread uses. | 
|  |  | 
|  | void Watchdog::ThreadDelegate::ThreadMain() { | 
|  | SetThreadName(); | 
|  | TimeDelta remaining_duration; | 
|  | StaticData* static_data = GetStaticData(); | 
|  | while (1) { | 
|  | AutoLock lock(watchdog_->lock_); | 
|  | while (DISARMED == watchdog_->state_) | 
|  | watchdog_->condition_variable_.Wait(); | 
|  | if (SHUTDOWN == watchdog_->state_) { | 
|  | watchdog_->state_ = JOINABLE; | 
|  | return; | 
|  | } | 
|  | DCHECK(ARMED == watchdog_->state_); | 
|  | remaining_duration = watchdog_->duration_ - | 
|  | (TimeTicks::Now() - watchdog_->start_time_); | 
|  | if (remaining_duration.InMilliseconds() > 0) { | 
|  | // Spurios wake?  Timer drifts?  Go back to sleep for remaining time. | 
|  | watchdog_->condition_variable_.TimedWait(remaining_duration); | 
|  | continue; | 
|  | } | 
|  | // 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_data->lock); | 
|  | if (static_data->last_debugged_alarm_time > watchdog_->start_time_) { | 
|  | // False alarm: we started our clock before the debugger break (last | 
|  | // alarm time). | 
|  | watchdog_->start_time_ += static_data->last_debugged_alarm_delay; | 
|  | if (static_data->last_debugged_alarm_time > watchdog_->start_time_) | 
|  | // Too many alarms must have taken place. | 
|  | watchdog_->state_ = DISARMED; | 
|  | continue; | 
|  | } | 
|  | } | 
|  | watchdog_->state_ = DISARMED;  // Only alarm at most once. | 
|  | TimeTicks last_alarm_time = TimeTicks::Now(); | 
|  | { | 
|  | AutoUnlock unlock(watchdog_->lock_); | 
|  | watchdog_->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)) | 
|  | continue; | 
|  | // Ignore race of two alarms/breaks going off at roughly the same time. | 
|  | AutoLock static_lock(static_data->lock); | 
|  | // This was a real debugger break. | 
|  | static_data->last_debugged_alarm_time = last_alarm_time; | 
|  | static_data->last_debugged_alarm_delay = last_alarm_delay; | 
|  | } | 
|  | } | 
|  |  | 
|  | void Watchdog::ThreadDelegate::SetThreadName() const { | 
|  | std::string name = watchdog_->thread_watched_name_ + " Watchdog"; | 
|  | PlatformThread::SetName(name); | 
|  | DVLOG(1) << "Watchdog active: " << name; | 
|  | } | 
|  |  | 
|  | // static | 
|  | void Watchdog::ResetStaticData() { | 
|  | StaticData* static_data = GetStaticData(); | 
|  | AutoLock lock(static_data->lock); | 
|  | // See https://crbug.com/734232 for why this cannot be zero-initialized. | 
|  | static_data->last_debugged_alarm_time = TimeTicks::Min(); | 
|  | static_data->last_debugged_alarm_delay = TimeDelta(); | 
|  | } | 
|  |  | 
|  | }  // namespace base |