1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/xpcom/threads/BackgroundHangMonitor.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,210 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#ifndef mozilla_BackgroundHangMonitor_h 1.10 +#define mozilla_BackgroundHangMonitor_h 1.11 + 1.12 +#include "mozilla/RefPtr.h" 1.13 +#include "mozilla/Monitor.h" 1.14 + 1.15 +#include <stdint.h> 1.16 + 1.17 +namespace mozilla { 1.18 + 1.19 +namespace Telemetry { 1.20 +class ThreadHangStats; 1.21 +}; 1.22 + 1.23 +// Disabled for Beta/Release builds because of bug 965392. 1.24 +// Disabled for debug builds because of bug 979069. 1.25 +#if !defined(RELEASE_BUILD) && !defined(DEBUG) 1.26 +// Undefine to disable background hang monitor 1.27 +#define MOZ_ENABLE_BACKGROUND_HANG_MONITOR 1.28 +#endif 1.29 + 1.30 +class BackgroundHangThread; 1.31 + 1.32 +/** 1.33 + * The background hang monitor is responsible for detecting and reporting 1.34 + * hangs in background (non-main) threads. A thread registers itself using 1.35 + * the BackgroundHangMonitor object and periodically calls its methods to 1.36 + * inform the hang monitor of the thread's activity. Each thread is given 1.37 + * a thread name, a timeout, and a maximum timeout. If one of the thread's 1.38 + * tasks runs for longer than the timeout duration but shorter than the 1.39 + * maximum timeout, a (transient) hang is reported. On the other hand, if 1.40 + * a task runs for longer than the maximum timeout duration or never 1.41 + * finishes (e.g. in a deadlock), a permahang is reported. 1.42 + * 1.43 + * Tasks are defined arbitrarily, but are typically represented by events 1.44 + * in an event loop -- processing one event is equivalent to running one 1.45 + * task. To ensure responsiveness, tasks in a thread often have a target 1.46 + * running time. This is a good starting point for determining the timeout 1.47 + * and maximum timeout values. For example, the Compositor thread has a 1.48 + * responsiveness goal of 60Hz or 17ms, so a starting timeout could be 1.49 + * 100ms. Considering some platforms (e.g. Android) can terminate the app 1.50 + * when a critical thread hangs for longer than a few seconds, a good 1.51 + * starting maximum timeout is 4 or 5 seconds. 1.52 + * 1.53 + * A thread registers itself through the BackgroundHangMonitor constructor. 1.54 + * Multiple BackgroundHangMonitor objects can be used in one thread. The 1.55 + * constructor without arguments can be used when it is known that the thread 1.56 + * already has a BackgroundHangMonitor registered. When all instances of 1.57 + * BackgroundHangMonitor are destroyed, the thread is unregistered. 1.58 + * 1.59 + * The thread then uses two methods to inform BackgroundHangMonitor of the 1.60 + * thread's activity: 1.61 + * 1.62 + * > BackgroundHangMonitor::NotifyActivity should be called *before* 1.63 + * starting a task. The task run time is determined by the interval 1.64 + * between this call and the next NotifyActivity call. 1.65 + * 1.66 + * > BackgroundHangMonitor::NotifyWait should be called *before* the 1.67 + * thread enters a wait state (e.g. to wait for a new event). This 1.68 + * prevents a waiting thread from being detected as hanging. The wait 1.69 + * state is automatically cleared at the next NotifyActivity call. 1.70 + * 1.71 + * The following example shows hang monitoring in a simple event loop: 1.72 + * 1.73 + * void thread_main() 1.74 + * { 1.75 + * mozilla::BackgroundHangMonitor hangMonitor("example1", 100, 1000); 1.76 + * while (!exiting) { 1.77 + * hangMonitor.NotifyActivity(); 1.78 + * process_next_event(); 1.79 + * hangMonitor.NotifyWait(); 1.80 + * wait_for_next_event(); 1.81 + * } 1.82 + * } 1.83 + * 1.84 + * The following example shows reentrancy in nested event loops: 1.85 + * 1.86 + * void thread_main() 1.87 + * { 1.88 + * mozilla::BackgroundHangMonitor hangMonitor("example2", 100, 1000); 1.89 + * while (!exiting) { 1.90 + * hangMonitor.NotifyActivity(); 1.91 + * process_next_event(); 1.92 + * hangMonitor.NotifyWait(); 1.93 + * wait_for_next_event(); 1.94 + * } 1.95 + * } 1.96 + * 1.97 + * void process_next_event() 1.98 + * { 1.99 + * mozilla::BackgroundHangMonitor hangMonitor(); 1.100 + * if (is_sync_event) { 1.101 + * while (!finished_event) { 1.102 + * hangMonitor.NotifyActivity(); 1.103 + * process_next_event(); 1.104 + * hangMonitor.NotifyWait(); 1.105 + * wait_for_next_event(); 1.106 + * } 1.107 + * } else { 1.108 + * process_nonsync_event(); 1.109 + * } 1.110 + * } 1.111 + * 1.112 + */ 1.113 +class BackgroundHangMonitor 1.114 +{ 1.115 +private: 1.116 + RefPtr<BackgroundHangThread> mThread; 1.117 + 1.118 +public: 1.119 + static const uint32_t kNoTimeout = 0; 1.120 + 1.121 + /** 1.122 + * ThreadHangStatsIterator is used to iterate through the ThreadHangStats 1.123 + * associated with each active monitored thread. Because of an internal 1.124 + * lock while this object is alive, a thread must use only one instance 1.125 + * of this class at a time and must iterate through the list as fast as 1.126 + * possible. The following example shows using the iterator: 1.127 + * 1.128 + * { 1.129 + * // Scope the iter variable so it's destroyed as soon as we're done 1.130 + * BackgroundHangMonitor::ThreadHangStatsIterator iter; 1.131 + * for (ThreadHangStats* histogram = iter.GetNext(); 1.132 + * histogram; histogram = iter.GetNext()) { 1.133 + * // Process histogram 1.134 + * } 1.135 + * } 1.136 + */ 1.137 + class ThreadHangStatsIterator : public MonitorAutoLock 1.138 + { 1.139 + private: 1.140 + BackgroundHangThread* mThread; 1.141 + 1.142 + ThreadHangStatsIterator(const ThreadHangStatsIterator&); 1.143 + ThreadHangStatsIterator& operator=(const ThreadHangStatsIterator&); 1.144 + 1.145 + public: 1.146 + /** 1.147 + * Create an ThreadHangStatsIterator instance and take the internal lock. 1.148 + * Internal lock is released on destruction. 1.149 + */ 1.150 + ThreadHangStatsIterator(); 1.151 + 1.152 + /** 1.153 + * Get the next item in the list; the first call returns the first item. 1.154 + * Returns nullptr at the end of the list. 1.155 + */ 1.156 + Telemetry::ThreadHangStats* GetNext(); 1.157 + }; 1.158 + 1.159 + /** 1.160 + * Enable hang monitoring. 1.161 + * Must return before using BackgroundHangMonitor. 1.162 + */ 1.163 + static void Startup(); 1.164 + 1.165 + /** 1.166 + * Disable hang monitoring. 1.167 + * Can be called without destroying all BackgroundHangMonitors first. 1.168 + */ 1.169 + static void Shutdown(); 1.170 + 1.171 + /** 1.172 + * Start monitoring hangs for the current thread. 1.173 + * 1.174 + * @param aName Name to identify the thread with 1.175 + * @param aTimeoutMs Amount of time in milliseconds without 1.176 + * activity before registering a hang 1.177 + * @param aMaxTimeoutMs Amount of time in milliseconds without 1.178 + * activity before registering a permanent hang 1.179 + */ 1.180 + BackgroundHangMonitor(const char* aName, 1.181 + uint32_t aTimeoutMs, 1.182 + uint32_t aMaxTimeoutMs); 1.183 + 1.184 + /** 1.185 + * Monitor hangs using an existing monitor 1.186 + * associated with the current thread. 1.187 + */ 1.188 + BackgroundHangMonitor(); 1.189 + 1.190 + /** 1.191 + * Destroys the hang monitor; hang monitoring for a thread stops 1.192 + * when all monitors associated with the thread are destroyed. 1.193 + */ 1.194 + ~BackgroundHangMonitor(); 1.195 + 1.196 + /** 1.197 + * Notify the hang monitor of pending current thread activity. 1.198 + * Call this method before starting an "activity" or after 1.199 + * exiting from a wait state. 1.200 + */ 1.201 + void NotifyActivity(); 1.202 + 1.203 + /** 1.204 + * Notify the hang monitor of current thread wait. 1.205 + * Call this method before entering a wait state; call 1.206 + * NotifyActivity when subsequently exiting the wait state. 1.207 + */ 1.208 + void NotifyWait(); 1.209 +}; 1.210 + 1.211 +} // namespace mozilla 1.212 + 1.213 +#endif // mozilla_BackgroundHangMonitor_h