blob: 4a50cffe05976ce85551a0f412c6f2dabb28f60c [file] [log] [blame]
// 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.
// This file defines a WatchDog thread that monitors the responsiveness of other
// browser threads like UI, IO, DB, FILE and CACHED threads. It also defines
// ThreadWatcher class which performs health check on threads that would like to
// be watched. This file also defines ThreadWatcherList class that has list of
// all active ThreadWatcher objects.
//
// ThreadWatcher class sends ping message to the watched thread and the watched
// thread responds back with a pong message. It uploads response time
// (difference between ping and pong times) as a histogram.
//
// TODO(raman): ThreadWatcher can detect hung threads. If a hung thread is
// detected, we should probably just crash, and allow the crash system to gather
// then stack trace.
//
// Example Usage:
//
// The following is an example for watching responsiveness of watched (IO)
// thread. |sleep_time| specifies how often ping messages have to be sent to
// watched (IO) thread. |unresponsive_time| is the wait time after ping
// message is sent, to check if we have received pong message or not.
// |unresponsive_threshold| specifies the number of unanswered ping messages
// after which watched (IO) thread is considered as not responsive.
// |crash_on_hang| specifies if we want to crash the browser when the watched
// (IO) thread has become sufficiently unresponsive, while other threads are
// sufficiently responsive. |live_threads_threshold| specifies the number of
// browser threads that are to be responsive when we want to crash the browser
// because of hung watched (IO) thread.
//
// base::TimeDelta sleep_time = base::TimeDelta::FromSeconds(5);
// base::TimeDelta unresponsive_time = base::TimeDelta::FromSeconds(10);
// uint32_t unresponsive_threshold = ThreadWatcherList::kUnresponsiveCount;
// bool crash_on_hang = false;
// uint32_t live_threads_threshold = ThreadWatcherList::kLiveThreadsThreshold;
// ThreadWatcher::StartWatching(
// BrowserThread::IO, "IO", sleep_time, unresponsive_time,
// unresponsive_threshold, crash_on_hang, live_threads_threshold);
#ifndef CHROME_BROWSER_METRICS_THREAD_WATCHER_H_
#define CHROME_BROWSER_METRICS_THREAD_WATCHER_H_
#include <map>
#include <string>
#include <vector>
#include <stdint.h>
#include "base/command_line.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/lock.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread.h"
#include "base/threading/watchdog.h"
#include "base/time/time.h"
#include "components/metrics/call_stack_profile_params.h"
#include "components/omnibox/browser/omnibox_event_global_tracker.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
class CustomThreadWatcher;
class StartupTimeBomb;
class ThreadWatcherList;
class ThreadWatcherObserver;
// This class performs health check on threads that would like to be watched.
class ThreadWatcher {
public:
// base::Bind supports methods with up to 6 parameters. WatchingParams is used
// as a workaround that limitation for invoking ThreadWatcher::StartWatching.
struct WatchingParams {
content::BrowserThread::ID thread_id;
std::string thread_name;
base::TimeDelta sleep_time;
base::TimeDelta unresponsive_time;
uint32_t unresponsive_threshold;
bool crash_on_hang;
uint32_t live_threads_threshold;
WatchingParams(const content::BrowserThread::ID& thread_id_in,
const std::string& thread_name_in,
const base::TimeDelta& sleep_time_in,
const base::TimeDelta& unresponsive_time_in,
uint32_t unresponsive_threshold_in,
bool crash_on_hang_in,
uint32_t live_threads_threshold_in)
: thread_id(thread_id_in),
thread_name(thread_name_in),
sleep_time(sleep_time_in),
unresponsive_time(unresponsive_time_in),
unresponsive_threshold(unresponsive_threshold_in),
crash_on_hang(crash_on_hang_in),
live_threads_threshold(live_threads_threshold_in) {}
};
virtual ~ThreadWatcher();
// This method starts performing health check on the given |thread_id|. It
// will create ThreadWatcher object for the given |thread_id|, |thread_name|.
// |sleep_time| is the wait time between ping messages. |unresponsive_time| is
// the wait time after ping message is sent, to check if we have received pong
// message or not. |unresponsive_threshold| is used to determine if the thread
// is responsive or not. The watched thread is considered unresponsive if it
// hasn't responded with a pong message for |unresponsive_threshold| number of
// ping messages. |crash_on_hang| specifies if browser should be crashed when
// the watched thread is unresponsive. |live_threads_threshold| specifies the
// number of browser threads that are to be responsive when we want to crash
// the browser and watched thread has become sufficiently unresponsive. It
// will register that ThreadWatcher object and activate the thread watching of
// the given thread_id.
static void StartWatching(const WatchingParams& params);
// Return the |thread_id_| of the thread being watched.
content::BrowserThread::ID thread_id() const { return thread_id_; }
// Return the name of the thread being watched.
std::string thread_name() const { return thread_name_; }
// Return the sleep time between ping messages to be sent to the thread.
base::TimeDelta sleep_time() const { return sleep_time_; }
// Return the the wait time to check the responsiveness of the thread.
base::TimeDelta unresponsive_time() const { return unresponsive_time_; }
// Returns true if we are montioring the thread.
bool active() const { return active_; }
// Returns |ping_time_| (used by unit tests).
base::TimeTicks ping_time() const { return ping_time_; }
// Returns |ping_sequence_number_| (used by unit tests).
uint64_t ping_sequence_number() const { return ping_sequence_number_; }
protected:
// Construct a ThreadWatcher for the given |thread_id|. |sleep_time| is the
// wait time between ping messages. |unresponsive_time| is the wait time after
// ping message is sent, to check if we have received pong message or not.
explicit ThreadWatcher(const WatchingParams& params);
// This method activates the thread watching which starts ping/pong messaging.
virtual void ActivateThreadWatching();
// This method de-activates the thread watching and revokes all tasks.
virtual void DeActivateThreadWatching();
// This will ensure that the watching is actively taking place, and awaken
// (i.e., post a PostPingMessage()) if the watcher has stopped pinging due to
// lack of user activity. It will also reset |ping_count_| to
// |unresponsive_threshold_|.
virtual void WakeUp();
// This method records when ping message was sent and it will Post a task
// (OnPingMessage()) to the watched thread that does nothing but respond with
// OnPongMessage(). It also posts a task (OnCheckResponsiveness()) to check
// responsiveness of monitored thread that would be called after waiting
// |unresponsive_time_|.
// This method is accessible on WatchDogThread.
virtual void PostPingMessage();
// This method handles a Pong Message from watched thread. It will track the
// response time (pong time minus ping time) via histograms. It posts a
// PostPingMessage() task that would be called after waiting |sleep_time_|. It
// increments |ping_sequence_number_| by 1.
// This method is accessible on WatchDogThread.
virtual void OnPongMessage(uint64_t ping_sequence_number);
// This method will determine if the watched thread is responsive or not. If
// the latest |ping_sequence_number_| is not same as the
// |ping_sequence_number| that is passed in, then we can assume that watched
// thread has responded with a pong message.
// This method is accessible on WatchDogThread.
virtual void OnCheckResponsiveness(uint64_t ping_sequence_number);
// Set by OnCheckResponsiveness when it determines if the watched thread is
// responsive or not.
bool responsive_;
private:
friend class ThreadWatcherList;
friend class CustomThreadWatcher;
// Allow tests to access our innards for testing purposes.
FRIEND_TEST_ALL_PREFIXES(ThreadWatcherTest, Registration);
FRIEND_TEST_ALL_PREFIXES(ThreadWatcherTest, ThreadResponding);
FRIEND_TEST_ALL_PREFIXES(ThreadWatcherTest, ThreadNotResponding);
FRIEND_TEST_ALL_PREFIXES(ThreadWatcherTest, MultipleThreadsResponding);
FRIEND_TEST_ALL_PREFIXES(ThreadWatcherTest, MultipleThreadsNotResponding);
// Post constructor initialization.
void Initialize();
// Watched thread does nothing except post callback_task to the WATCHDOG
// Thread. This method is called on watched thread.
static void OnPingMessage(const content::BrowserThread::ID& thread_id,
const base::Closure& callback_task);
// This method resets |unresponsive_count_| to zero because watched thread is
// responding to the ping message with a pong message.
void ResetHangCounters();
// This method records watched thread is not responding to the ping message.
// It increments |unresponsive_count_| by 1.
void GotNoResponse();
// This method returns true if the watched thread has not responded with a
// pong message for |unresponsive_threshold_| number of ping messages.
bool IsVeryUnresponsive();
// The |thread_id_| of the thread being watched. Only one instance can exist
// for the given |thread_id_| of the thread being watched.
const content::BrowserThread::ID thread_id_;
// The name of the thread being watched.
const std::string thread_name_;
// Used to post messages to watched thread.
scoped_refptr<base::SingleThreadTaskRunner> watched_runner_;
// It is the sleep time between the receipt of a pong message back, and the
// sending of another ping message.
const base::TimeDelta sleep_time_;
// It is the duration from sending a ping message, until we check status to be
// sure a pong message has been returned.
const base::TimeDelta unresponsive_time_;
// This is the last time when ping message was sent.
base::TimeTicks ping_time_;
// This is the last time when we got pong message.
base::TimeTicks pong_time_;
// This is the sequence number of the next ping for which there is no pong. If
// the instance is sleeping, then it will be the sequence number for the next
// ping.
uint64_t ping_sequence_number_;
// This is set to true if thread watcher is watching.
bool active_;
// The counter tracks least number of ping messages that will be sent to
// watched thread before the ping-pong mechanism will go into an extended
// sleep. If this value is zero, then the mechanism is in an extended sleep,
// and awaiting some observed user action before continuing.
int ping_count_;
// Histogram that keeps track of response times for the watched thread.
base::HistogramBase* response_time_histogram_;
// Histogram that keeps track of unresponsive time since the last pong message
// when we got no response (GotNoResponse()) from the watched thread.
base::HistogramBase* unresponsive_time_histogram_;
// Histogram that keeps track of how many threads are responding when we got
// no response (GotNoResponse()) from the watched thread.
base::HistogramBase* responsive_count_histogram_;
// Histogram that keeps track of how many threads are not responding when we
// got no response (GotNoResponse()) from the watched thread. Count includes
// the thread that got no response.
base::HistogramBase* unresponsive_count_histogram_;
// This counter tracks the unresponsiveness of watched thread. If this value
// is zero then watched thread has responded with a pong message. This is
// incremented by 1 when we got no response (GotNoResponse()) from the watched
// thread.
uint32_t unresponsive_count_;
// This is set to true when we would have crashed the browser because the
// watched thread hasn't responded at least |unresponsive_threshold_| times.
// It is reset to false when watched thread responds with a pong message.
bool hung_processing_complete_;
// This is used to determine if the watched thread is responsive or not. If
// watched thread's |unresponsive_count_| is greater than or equal to
// |unresponsive_threshold_| then we would consider it as unresponsive.
uint32_t unresponsive_threshold_;
// This is set to true if we want to crash the browser when the watched thread
// has become sufficiently unresponsive, while other threads are sufficiently
// responsive.
bool crash_on_hang_;
// This specifies the number of browser threads that are to be responsive when
// we want to crash the browser because watched thread has become sufficiently
// unresponsive.
uint32_t live_threads_threshold_;
// We use this factory to create callback tasks for ThreadWatcher object. We
// use this during ping-pong messaging between WatchDog thread and watched
// thread.
base::WeakPtrFactory<ThreadWatcher> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(ThreadWatcher);
};
// Class with a list of all active thread watchers. A thread watcher is active
// if it has been registered, which includes determing the histogram name. This
// class provides utility functions to start and stop watching all browser
// threads. Only one instance of this class exists.
class ThreadWatcherList {
public:
// A map from BrowserThread to the actual instances.
typedef std::map<content::BrowserThread::ID, ThreadWatcher*> RegistrationList;
// A map from thread names (UI, IO, etc) to |CrashDataThresholds|.
// |live_threads_threshold| specifies the maximum number of browser threads
// that have to be responsive when we want to crash the browser because of
// hung watched thread. This threshold allows us to either look for a system
// deadlock, or look for a solo hung thread. A small live_threads_threshold
// looks for a broad deadlock (few browser threads left running), and a large
// threshold looks for a single hung thread (this in only appropriate for a
// thread that *should* never have much jank, such as the IO).
//
// |unresponsive_threshold| specifies the number of unanswered ping messages
// after which watched (UI, IO, etc) thread is considered as not responsive.
// We translate "time" (given in seconds) into a number of pings. As a result,
// we only declare a thread unresponsive when a lot of "time" has passed (many
// pings), and yet our pinging thread has continued to process messages (so we
// know the entire PC is not hung). Set this number higher to crash less
// often, and lower to crash more often.
//
// The map lists all threads (by name) that can induce a crash by hanging. It
// is populated from the command line, or given a default list. See
// InitializeAndStartWatching() for the separate list of all threads that are
// watched, as they provide the system context of how hung *other* threads
// are.
//
// ThreadWatcher monitors five browser threads (i.e., UI, IO, DB, FILE,
// and CACHE). Out of the 5 threads, any subset may be watched, to potentially
// cause a crash. The following example's command line causes exactly 3
// threads to be watched.
//
// The example command line argument consists of "UI:3:18,IO:3:18,FILE:5:90".
// In that string, the first parameter specifies the thread_id: UI, IO or
// FILE. The second parameter specifies |live_threads_threshold|. For UI and
// IO threads, we would crash if the number of threads responding is less than
// or equal to 3. The third parameter specifies the unresponsive threshold
// seconds. This number is used to calculate |unresponsive_threshold|. In this
// example for UI and IO threads, we would crash if those threads don't
// respond for 18 seconds (or 9 unanswered ping messages) and for FILE thread,
// crash_seconds is set to 90 seconds (or 45 unanswered ping messages).
//
// The following examples explain how the data in |CrashDataThresholds|
// controls the crashes.
//
// Example 1: If the |live_threads_threshold| value for "IO" was 3 and
// unresponsive threshold seconds is 18 (or |unresponsive_threshold| is 9),
// then we would crash if the IO thread was hung (9 unanswered ping messages)
// and if at least one thread is responding and total responding threads is
// less than or equal to 3 (this thread, plus at least one other thread is
// unresponsive). We would not crash if none of the threads are responding, as
// we'd assume such large hang counts mean that the system is generally
// unresponsive.
// Example 2: If the |live_threads_threshold| value for "UI" was any number
// higher than 6 and unresponsive threshold seconds is 18 (or
// |unresponsive_threshold| is 9), then we would always crash if the UI thread
// was hung (9 unanswered ping messages), no matter what the other threads are
// doing.
// Example 3: If the |live_threads_threshold| value of "FILE" was 5 and
// unresponsive threshold seconds is 90 (or |unresponsive_threshold| is 45),
// then we would only crash if the FILE thread was the ONLY hung thread
// (because we watch 6 threads). If there was another unresponsive thread, we
// would not consider this a problem worth crashing for. FILE thread would be
// considered as hung if it didn't respond for 45 ping messages.
struct CrashDataThresholds {
CrashDataThresholds(uint32_t live_threads_threshold,
uint32_t unresponsive_threshold);
CrashDataThresholds();
uint32_t live_threads_threshold;
uint32_t unresponsive_threshold;
};
typedef std::map<std::string, CrashDataThresholds> CrashOnHangThreadMap;
// This method posts a task on WatchDogThread to start watching all browser
// threads.
// This method is accessible on UI thread.
static void StartWatchingAll(const base::CommandLine& command_line);
// This method posts a task on WatchDogThread to RevokeAll tasks and to
// deactive thread watching of other threads and tell NotificationService to
// stop calling Observe.
// This method is accessible on UI thread.
static void StopWatchingAll();
// Register() stores a pointer to the given ThreadWatcher in a global map.
// Returns the pointer if it was successfully registered, null otherwise.
static ThreadWatcher* Register(std::unique_ptr<ThreadWatcher> watcher);
// This method returns number of responsive and unresponsive watched threads.
static void GetStatusOfThreads(uint32_t* responding_thread_count,
uint32_t* unresponding_thread_count);
// This will ensure that the watching is actively taking place, and awaken
// all thread watchers that are registered.
static void WakeUpAll();
private:
// Allow tests to access our innards for testing purposes.
friend class CustomThreadWatcher;
friend class ThreadWatcherListTest;
friend class ThreadWatcherTest;
FRIEND_TEST_ALL_PREFIXES(ThreadWatcherAndroidTest,
ApplicationStatusNotification);
FRIEND_TEST_ALL_PREFIXES(ThreadWatcherListTest, Restart);
FRIEND_TEST_ALL_PREFIXES(ThreadWatcherTest, ThreadNamesOnlyArgs);
FRIEND_TEST_ALL_PREFIXES(ThreadWatcherTest, ThreadNamesAndLiveThresholdArgs);
FRIEND_TEST_ALL_PREFIXES(ThreadWatcherTest, CrashOnHangThreadsAllArgs);
// This singleton holds the global list of registered ThreadWatchers.
ThreadWatcherList();
// Destructor deletes all registered ThreadWatcher instances.
virtual ~ThreadWatcherList();
// Parses the command line to get |crash_on_hang_threads| map from
// switches::kCrashOnHangThreads. |crash_on_hang_threads| is a map of
// |crash_on_hang| thread's names to |CrashDataThresholds|.
static void ParseCommandLine(const base::CommandLine& command_line,
uint32_t* unresponsive_threshold,
CrashOnHangThreadMap* crash_on_hang_threads);
// Parses the argument |crash_on_hang_thread_names| and creates
// |crash_on_hang_threads| map of |crash_on_hang| thread's names to
// |CrashDataThresholds|. If |crash_on_hang_thread_names| doesn't specify
// |live_threads_threshold|, then it uses |default_live_threads_threshold| as
// the value. If |crash_on_hang_thread_names| doesn't specify |crash_seconds|,
// then it uses |default_crash_seconds| as the value.
static void ParseCommandLineCrashOnHangThreads(
const std::string& crash_on_hang_thread_names,
uint32_t default_live_threads_threshold,
uint32_t default_crash_seconds,
CrashOnHangThreadMap* crash_on_hang_threads);
// This constructs the |ThreadWatcherList| singleton and starts watching
// browser threads by calling StartWatching() on each browser thread that is
// watched. It disarms StartupTimeBomb.
static void InitializeAndStartWatching(
uint32_t unresponsive_threshold,
const CrashOnHangThreadMap& crash_on_hang_threads);
// This method calls ThreadWatcher::StartWatching() to perform health check on
// the given |thread_id|.
static void StartWatching(const content::BrowserThread::ID& thread_id,
const std::string& thread_name,
const base::TimeDelta& sleep_time,
const base::TimeDelta& unresponsive_time,
uint32_t unresponsive_threshold,
const CrashOnHangThreadMap& crash_on_hang_threads);
// Delete all thread watcher objects and remove them from global map. It also
// deletes |g_thread_watcher_list_|.
static void DeleteAll();
// The Find() method can be used to test to see if a given ThreadWatcher was
// already registered, or to retrieve a pointer to it from the global map.
static ThreadWatcher* Find(const content::BrowserThread::ID& thread_id);
// Sets |g_stopped_| on the WatchDogThread. This is necessary to reflect the
// state between the delayed |StartWatchingAll| and the immediate
// |StopWatchingAll|.
static void SetStopped(bool stopped);
// The singleton of this class and is used to keep track of information about
// threads that are being watched.
static ThreadWatcherList* g_thread_watcher_list_;
// StartWatchingAll() is delayed in relation to StopWatchingAll(), so if
// a Stop comes first, prevent further initialization.
static bool g_stopped_;
// This is the wait time between ping messages.
static const int kSleepSeconds;
// This is the wait time after ping message is sent, to check if we have
// received pong message or not.
static const int kUnresponsiveSeconds;
// Default values for |unresponsive_threshold|.
static const int kUnresponsiveCount;
// Default values for |live_threads_threshold|.
static const int kLiveThreadsThreshold;
// Default value for the delay until |InitializeAndStartWatching| is called.
// Non-const for tests.
static int g_initialize_delay_seconds;
// Map of all registered watched threads, from thread_id to ThreadWatcher.
RegistrationList registered_;
DISALLOW_COPY_AND_ASSIGN(ThreadWatcherList);
};
// This class ensures that the thread watching is actively taking place. Only
// one instance of this class exists.
class ThreadWatcherObserver : public content::NotificationObserver {
public:
// Registers |g_thread_watcher_observer_| as the Notifications observer.
// |wakeup_interval| specifies how often to wake up thread watchers. This
// method is accessible on UI thread.
static void SetupNotifications(const base::TimeDelta& wakeup_interval);
// Removes all ints from |registrar_| and deletes
// |g_thread_watcher_observer_|. This method is accessible on UI thread.
static void RemoveNotifications();
private:
// Constructor of |g_thread_watcher_observer_| singleton.
explicit ThreadWatcherObserver(const base::TimeDelta& wakeup_interval);
// Destructor of |g_thread_watcher_observer_| singleton.
~ThreadWatcherObserver() override;
// This ensures all thread watchers are active because there is some user
// activity. It will wake up all thread watchers every |wakeup_interval_|
// seconds. This is the implementation of content::NotificationObserver. When
// a matching notification is posted to the notification service, this method
// is called.
void Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) override;
// Called when a URL is opened from the Omnibox.
void OnURLOpenedFromOmnibox(OmniboxLog* log);
// Called when user activity is detected.
void OnUserActivityDetected();
// The singleton of this class.
static ThreadWatcherObserver* g_thread_watcher_observer_;
// The registrar that holds ints to be observed.
content::NotificationRegistrar registrar_;
// This is the last time when woke all thread watchers up.
base::TimeTicks last_wakeup_time_;
// It is the time interval between wake up calls to thread watchers.
const base::TimeDelta wakeup_interval_;
// Subscription for receiving callbacks that a URL was opened from the
// omnibox.
std::unique_ptr<base::CallbackList<void(OmniboxLog*)>::Subscription>
omnibox_url_opened_subscription_;
DISALLOW_COPY_AND_ASSIGN(ThreadWatcherObserver);
};
// Class for WatchDogThread and in its Init method, we start watching UI, IO,
// DB, FILE, CACHED threads.
class WatchDogThread : public base::Thread {
public:
// Constructor.
WatchDogThread();
// Destroys the thread and stops the thread.
~WatchDogThread() override;
// Callable on any thread. Returns whether you're currently on a
// WatchDogThread.
static bool CurrentlyOnWatchDogThread();
// These are the same methods in message_loop.h, but are guaranteed to either
// get posted to the MessageLoop if it's still alive, or be deleted otherwise.
// They return true iff the watchdog thread existed and the task was posted.
// Note that even if the task is posted, there's no guarantee that it will
// run, since the target thread may already have a Quit message in its queue.
static bool PostTask(const tracked_objects::Location& from_here,
const base::Closure& task);
static bool PostDelayedTask(const tracked_objects::Location& from_here,
const base::Closure& task,
base::TimeDelta delay);
protected:
void Init() override;
void CleanUp() override;
private:
// This method returns true if Init() is called.
bool Started() const;
static bool PostTaskHelper(
const tracked_objects::Location& from_here,
const base::Closure& task,
base::TimeDelta delay);
DISALLOW_COPY_AND_ASSIGN(WatchDogThread);
};
// This is a wrapper class for getting the crash dumps of the hangs during
// startup.
class StartupTimeBomb {
public:
// This singleton is instantiated when the browser process is launched.
StartupTimeBomb();
// Destructor disarm's startup_watchdog_ (if it is arm'ed) so that alarm
// doesn't go off.
~StartupTimeBomb();
// Constructs |startup_watchdog_| which spawns a thread and starts timer.
// |duration| specifies how long |startup_watchdog_| will wait before it
// calls alarm.
void Arm(const base::TimeDelta& duration);
// Disarms |startup_watchdog_| thread and then deletes it which stops the
// Watchdog thread.
void Disarm();
// Disarms |g_startup_timebomb_|.
static void DisarmStartupTimeBomb();
private:
// Deletes the watchdog thread if it is joinable; otherwise it posts a delayed
// task to try again.
static void DeleteStartupWatchdog(const base::PlatformThreadId thread_id,
base::Watchdog* startup_watchdog);
// The singleton of this class.
static StartupTimeBomb* g_startup_timebomb_;
// Watches for hangs during startup until it is disarm'ed.
base::Watchdog* startup_watchdog_;
// The |thread_id_| on which this object is constructed.
const base::PlatformThreadId thread_id_;
DISALLOW_COPY_AND_ASSIGN(StartupTimeBomb);
};
// This is a wrapper class for detecting hangs during shutdown.
class ShutdownWatcherHelper {
public:
// Create an empty holder for |shutdown_watchdog_|.
ShutdownWatcherHelper();
// Destructor disarm's shutdown_watchdog_ so that alarm doesn't go off.
~ShutdownWatcherHelper();
// Constructs ShutdownWatchDogThread which spawns a thread and starts timer.
// |duration| specifies how long it will wait before it calls alarm.
void Arm(const base::TimeDelta& duration);
private:
// shutdown_watchdog_ watches for hangs during shutdown.
base::Watchdog* shutdown_watchdog_;
// The |thread_id_| on which this object is constructed.
const base::PlatformThreadId thread_id_;
DISALLOW_COPY_AND_ASSIGN(ShutdownWatcherHelper);
};
#endif // CHROME_BROWSER_METRICS_THREAD_WATCHER_H_