diff -r 000000000000 -r 6474c204b198 xpcom/threads/BackgroundHangMonitor.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xpcom/threads/BackgroundHangMonitor.h Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,210 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_BackgroundHangMonitor_h +#define mozilla_BackgroundHangMonitor_h + +#include "mozilla/RefPtr.h" +#include "mozilla/Monitor.h" + +#include + +namespace mozilla { + +namespace Telemetry { +class ThreadHangStats; +}; + +// Disabled for Beta/Release builds because of bug 965392. +// Disabled for debug builds because of bug 979069. +#if !defined(RELEASE_BUILD) && !defined(DEBUG) +// Undefine to disable background hang monitor +#define MOZ_ENABLE_BACKGROUND_HANG_MONITOR +#endif + +class BackgroundHangThread; + +/** + * The background hang monitor is responsible for detecting and reporting + * hangs in background (non-main) threads. A thread registers itself using + * the BackgroundHangMonitor object and periodically calls its methods to + * inform the hang monitor of the thread's activity. Each thread is given + * a thread name, a timeout, and a maximum timeout. If one of the thread's + * tasks runs for longer than the timeout duration but shorter than the + * maximum timeout, a (transient) hang is reported. On the other hand, if + * a task runs for longer than the maximum timeout duration or never + * finishes (e.g. in a deadlock), a permahang is reported. + * + * Tasks are defined arbitrarily, but are typically represented by events + * in an event loop -- processing one event is equivalent to running one + * task. To ensure responsiveness, tasks in a thread often have a target + * running time. This is a good starting point for determining the timeout + * and maximum timeout values. For example, the Compositor thread has a + * responsiveness goal of 60Hz or 17ms, so a starting timeout could be + * 100ms. Considering some platforms (e.g. Android) can terminate the app + * when a critical thread hangs for longer than a few seconds, a good + * starting maximum timeout is 4 or 5 seconds. + * + * A thread registers itself through the BackgroundHangMonitor constructor. + * Multiple BackgroundHangMonitor objects can be used in one thread. The + * constructor without arguments can be used when it is known that the thread + * already has a BackgroundHangMonitor registered. When all instances of + * BackgroundHangMonitor are destroyed, the thread is unregistered. + * + * The thread then uses two methods to inform BackgroundHangMonitor of the + * thread's activity: + * + * > BackgroundHangMonitor::NotifyActivity should be called *before* + * starting a task. The task run time is determined by the interval + * between this call and the next NotifyActivity call. + * + * > BackgroundHangMonitor::NotifyWait should be called *before* the + * thread enters a wait state (e.g. to wait for a new event). This + * prevents a waiting thread from being detected as hanging. The wait + * state is automatically cleared at the next NotifyActivity call. + * + * The following example shows hang monitoring in a simple event loop: + * + * void thread_main() + * { + * mozilla::BackgroundHangMonitor hangMonitor("example1", 100, 1000); + * while (!exiting) { + * hangMonitor.NotifyActivity(); + * process_next_event(); + * hangMonitor.NotifyWait(); + * wait_for_next_event(); + * } + * } + * + * The following example shows reentrancy in nested event loops: + * + * void thread_main() + * { + * mozilla::BackgroundHangMonitor hangMonitor("example2", 100, 1000); + * while (!exiting) { + * hangMonitor.NotifyActivity(); + * process_next_event(); + * hangMonitor.NotifyWait(); + * wait_for_next_event(); + * } + * } + * + * void process_next_event() + * { + * mozilla::BackgroundHangMonitor hangMonitor(); + * if (is_sync_event) { + * while (!finished_event) { + * hangMonitor.NotifyActivity(); + * process_next_event(); + * hangMonitor.NotifyWait(); + * wait_for_next_event(); + * } + * } else { + * process_nonsync_event(); + * } + * } + * + */ +class BackgroundHangMonitor +{ +private: + RefPtr mThread; + +public: + static const uint32_t kNoTimeout = 0; + + /** + * ThreadHangStatsIterator is used to iterate through the ThreadHangStats + * associated with each active monitored thread. Because of an internal + * lock while this object is alive, a thread must use only one instance + * of this class at a time and must iterate through the list as fast as + * possible. The following example shows using the iterator: + * + * { + * // Scope the iter variable so it's destroyed as soon as we're done + * BackgroundHangMonitor::ThreadHangStatsIterator iter; + * for (ThreadHangStats* histogram = iter.GetNext(); + * histogram; histogram = iter.GetNext()) { + * // Process histogram + * } + * } + */ + class ThreadHangStatsIterator : public MonitorAutoLock + { + private: + BackgroundHangThread* mThread; + + ThreadHangStatsIterator(const ThreadHangStatsIterator&); + ThreadHangStatsIterator& operator=(const ThreadHangStatsIterator&); + + public: + /** + * Create an ThreadHangStatsIterator instance and take the internal lock. + * Internal lock is released on destruction. + */ + ThreadHangStatsIterator(); + + /** + * Get the next item in the list; the first call returns the first item. + * Returns nullptr at the end of the list. + */ + Telemetry::ThreadHangStats* GetNext(); + }; + + /** + * Enable hang monitoring. + * Must return before using BackgroundHangMonitor. + */ + static void Startup(); + + /** + * Disable hang monitoring. + * Can be called without destroying all BackgroundHangMonitors first. + */ + static void Shutdown(); + + /** + * Start monitoring hangs for the current thread. + * + * @param aName Name to identify the thread with + * @param aTimeoutMs Amount of time in milliseconds without + * activity before registering a hang + * @param aMaxTimeoutMs Amount of time in milliseconds without + * activity before registering a permanent hang + */ + BackgroundHangMonitor(const char* aName, + uint32_t aTimeoutMs, + uint32_t aMaxTimeoutMs); + + /** + * Monitor hangs using an existing monitor + * associated with the current thread. + */ + BackgroundHangMonitor(); + + /** + * Destroys the hang monitor; hang monitoring for a thread stops + * when all monitors associated with the thread are destroyed. + */ + ~BackgroundHangMonitor(); + + /** + * Notify the hang monitor of pending current thread activity. + * Call this method before starting an "activity" or after + * exiting from a wait state. + */ + void NotifyActivity(); + + /** + * Notify the hang monitor of current thread wait. + * Call this method before entering a wait state; call + * NotifyActivity when subsequently exiting the wait state. + */ + void NotifyWait(); +}; + +} // namespace mozilla + +#endif // mozilla_BackgroundHangMonitor_h