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