Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #ifndef mozilla_BackgroundHangMonitor_h |
michael@0 | 7 | #define mozilla_BackgroundHangMonitor_h |
michael@0 | 8 | |
michael@0 | 9 | #include "mozilla/RefPtr.h" |
michael@0 | 10 | #include "mozilla/Monitor.h" |
michael@0 | 11 | |
michael@0 | 12 | #include <stdint.h> |
michael@0 | 13 | |
michael@0 | 14 | namespace mozilla { |
michael@0 | 15 | |
michael@0 | 16 | namespace Telemetry { |
michael@0 | 17 | class ThreadHangStats; |
michael@0 | 18 | }; |
michael@0 | 19 | |
michael@0 | 20 | // Disabled for Beta/Release builds because of bug 965392. |
michael@0 | 21 | // Disabled for debug builds because of bug 979069. |
michael@0 | 22 | #if !defined(RELEASE_BUILD) && !defined(DEBUG) |
michael@0 | 23 | // Undefine to disable background hang monitor |
michael@0 | 24 | #define MOZ_ENABLE_BACKGROUND_HANG_MONITOR |
michael@0 | 25 | #endif |
michael@0 | 26 | |
michael@0 | 27 | class BackgroundHangThread; |
michael@0 | 28 | |
michael@0 | 29 | /** |
michael@0 | 30 | * The background hang monitor is responsible for detecting and reporting |
michael@0 | 31 | * hangs in background (non-main) threads. A thread registers itself using |
michael@0 | 32 | * the BackgroundHangMonitor object and periodically calls its methods to |
michael@0 | 33 | * inform the hang monitor of the thread's activity. Each thread is given |
michael@0 | 34 | * a thread name, a timeout, and a maximum timeout. If one of the thread's |
michael@0 | 35 | * tasks runs for longer than the timeout duration but shorter than the |
michael@0 | 36 | * maximum timeout, a (transient) hang is reported. On the other hand, if |
michael@0 | 37 | * a task runs for longer than the maximum timeout duration or never |
michael@0 | 38 | * finishes (e.g. in a deadlock), a permahang is reported. |
michael@0 | 39 | * |
michael@0 | 40 | * Tasks are defined arbitrarily, but are typically represented by events |
michael@0 | 41 | * in an event loop -- processing one event is equivalent to running one |
michael@0 | 42 | * task. To ensure responsiveness, tasks in a thread often have a target |
michael@0 | 43 | * running time. This is a good starting point for determining the timeout |
michael@0 | 44 | * and maximum timeout values. For example, the Compositor thread has a |
michael@0 | 45 | * responsiveness goal of 60Hz or 17ms, so a starting timeout could be |
michael@0 | 46 | * 100ms. Considering some platforms (e.g. Android) can terminate the app |
michael@0 | 47 | * when a critical thread hangs for longer than a few seconds, a good |
michael@0 | 48 | * starting maximum timeout is 4 or 5 seconds. |
michael@0 | 49 | * |
michael@0 | 50 | * A thread registers itself through the BackgroundHangMonitor constructor. |
michael@0 | 51 | * Multiple BackgroundHangMonitor objects can be used in one thread. The |
michael@0 | 52 | * constructor without arguments can be used when it is known that the thread |
michael@0 | 53 | * already has a BackgroundHangMonitor registered. When all instances of |
michael@0 | 54 | * BackgroundHangMonitor are destroyed, the thread is unregistered. |
michael@0 | 55 | * |
michael@0 | 56 | * The thread then uses two methods to inform BackgroundHangMonitor of the |
michael@0 | 57 | * thread's activity: |
michael@0 | 58 | * |
michael@0 | 59 | * > BackgroundHangMonitor::NotifyActivity should be called *before* |
michael@0 | 60 | * starting a task. The task run time is determined by the interval |
michael@0 | 61 | * between this call and the next NotifyActivity call. |
michael@0 | 62 | * |
michael@0 | 63 | * > BackgroundHangMonitor::NotifyWait should be called *before* the |
michael@0 | 64 | * thread enters a wait state (e.g. to wait for a new event). This |
michael@0 | 65 | * prevents a waiting thread from being detected as hanging. The wait |
michael@0 | 66 | * state is automatically cleared at the next NotifyActivity call. |
michael@0 | 67 | * |
michael@0 | 68 | * The following example shows hang monitoring in a simple event loop: |
michael@0 | 69 | * |
michael@0 | 70 | * void thread_main() |
michael@0 | 71 | * { |
michael@0 | 72 | * mozilla::BackgroundHangMonitor hangMonitor("example1", 100, 1000); |
michael@0 | 73 | * while (!exiting) { |
michael@0 | 74 | * hangMonitor.NotifyActivity(); |
michael@0 | 75 | * process_next_event(); |
michael@0 | 76 | * hangMonitor.NotifyWait(); |
michael@0 | 77 | * wait_for_next_event(); |
michael@0 | 78 | * } |
michael@0 | 79 | * } |
michael@0 | 80 | * |
michael@0 | 81 | * The following example shows reentrancy in nested event loops: |
michael@0 | 82 | * |
michael@0 | 83 | * void thread_main() |
michael@0 | 84 | * { |
michael@0 | 85 | * mozilla::BackgroundHangMonitor hangMonitor("example2", 100, 1000); |
michael@0 | 86 | * while (!exiting) { |
michael@0 | 87 | * hangMonitor.NotifyActivity(); |
michael@0 | 88 | * process_next_event(); |
michael@0 | 89 | * hangMonitor.NotifyWait(); |
michael@0 | 90 | * wait_for_next_event(); |
michael@0 | 91 | * } |
michael@0 | 92 | * } |
michael@0 | 93 | * |
michael@0 | 94 | * void process_next_event() |
michael@0 | 95 | * { |
michael@0 | 96 | * mozilla::BackgroundHangMonitor hangMonitor(); |
michael@0 | 97 | * if (is_sync_event) { |
michael@0 | 98 | * while (!finished_event) { |
michael@0 | 99 | * hangMonitor.NotifyActivity(); |
michael@0 | 100 | * process_next_event(); |
michael@0 | 101 | * hangMonitor.NotifyWait(); |
michael@0 | 102 | * wait_for_next_event(); |
michael@0 | 103 | * } |
michael@0 | 104 | * } else { |
michael@0 | 105 | * process_nonsync_event(); |
michael@0 | 106 | * } |
michael@0 | 107 | * } |
michael@0 | 108 | * |
michael@0 | 109 | */ |
michael@0 | 110 | class BackgroundHangMonitor |
michael@0 | 111 | { |
michael@0 | 112 | private: |
michael@0 | 113 | RefPtr<BackgroundHangThread> mThread; |
michael@0 | 114 | |
michael@0 | 115 | public: |
michael@0 | 116 | static const uint32_t kNoTimeout = 0; |
michael@0 | 117 | |
michael@0 | 118 | /** |
michael@0 | 119 | * ThreadHangStatsIterator is used to iterate through the ThreadHangStats |
michael@0 | 120 | * associated with each active monitored thread. Because of an internal |
michael@0 | 121 | * lock while this object is alive, a thread must use only one instance |
michael@0 | 122 | * of this class at a time and must iterate through the list as fast as |
michael@0 | 123 | * possible. The following example shows using the iterator: |
michael@0 | 124 | * |
michael@0 | 125 | * { |
michael@0 | 126 | * // Scope the iter variable so it's destroyed as soon as we're done |
michael@0 | 127 | * BackgroundHangMonitor::ThreadHangStatsIterator iter; |
michael@0 | 128 | * for (ThreadHangStats* histogram = iter.GetNext(); |
michael@0 | 129 | * histogram; histogram = iter.GetNext()) { |
michael@0 | 130 | * // Process histogram |
michael@0 | 131 | * } |
michael@0 | 132 | * } |
michael@0 | 133 | */ |
michael@0 | 134 | class ThreadHangStatsIterator : public MonitorAutoLock |
michael@0 | 135 | { |
michael@0 | 136 | private: |
michael@0 | 137 | BackgroundHangThread* mThread; |
michael@0 | 138 | |
michael@0 | 139 | ThreadHangStatsIterator(const ThreadHangStatsIterator&); |
michael@0 | 140 | ThreadHangStatsIterator& operator=(const ThreadHangStatsIterator&); |
michael@0 | 141 | |
michael@0 | 142 | public: |
michael@0 | 143 | /** |
michael@0 | 144 | * Create an ThreadHangStatsIterator instance and take the internal lock. |
michael@0 | 145 | * Internal lock is released on destruction. |
michael@0 | 146 | */ |
michael@0 | 147 | ThreadHangStatsIterator(); |
michael@0 | 148 | |
michael@0 | 149 | /** |
michael@0 | 150 | * Get the next item in the list; the first call returns the first item. |
michael@0 | 151 | * Returns nullptr at the end of the list. |
michael@0 | 152 | */ |
michael@0 | 153 | Telemetry::ThreadHangStats* GetNext(); |
michael@0 | 154 | }; |
michael@0 | 155 | |
michael@0 | 156 | /** |
michael@0 | 157 | * Enable hang monitoring. |
michael@0 | 158 | * Must return before using BackgroundHangMonitor. |
michael@0 | 159 | */ |
michael@0 | 160 | static void Startup(); |
michael@0 | 161 | |
michael@0 | 162 | /** |
michael@0 | 163 | * Disable hang monitoring. |
michael@0 | 164 | * Can be called without destroying all BackgroundHangMonitors first. |
michael@0 | 165 | */ |
michael@0 | 166 | static void Shutdown(); |
michael@0 | 167 | |
michael@0 | 168 | /** |
michael@0 | 169 | * Start monitoring hangs for the current thread. |
michael@0 | 170 | * |
michael@0 | 171 | * @param aName Name to identify the thread with |
michael@0 | 172 | * @param aTimeoutMs Amount of time in milliseconds without |
michael@0 | 173 | * activity before registering a hang |
michael@0 | 174 | * @param aMaxTimeoutMs Amount of time in milliseconds without |
michael@0 | 175 | * activity before registering a permanent hang |
michael@0 | 176 | */ |
michael@0 | 177 | BackgroundHangMonitor(const char* aName, |
michael@0 | 178 | uint32_t aTimeoutMs, |
michael@0 | 179 | uint32_t aMaxTimeoutMs); |
michael@0 | 180 | |
michael@0 | 181 | /** |
michael@0 | 182 | * Monitor hangs using an existing monitor |
michael@0 | 183 | * associated with the current thread. |
michael@0 | 184 | */ |
michael@0 | 185 | BackgroundHangMonitor(); |
michael@0 | 186 | |
michael@0 | 187 | /** |
michael@0 | 188 | * Destroys the hang monitor; hang monitoring for a thread stops |
michael@0 | 189 | * when all monitors associated with the thread are destroyed. |
michael@0 | 190 | */ |
michael@0 | 191 | ~BackgroundHangMonitor(); |
michael@0 | 192 | |
michael@0 | 193 | /** |
michael@0 | 194 | * Notify the hang monitor of pending current thread activity. |
michael@0 | 195 | * Call this method before starting an "activity" or after |
michael@0 | 196 | * exiting from a wait state. |
michael@0 | 197 | */ |
michael@0 | 198 | void NotifyActivity(); |
michael@0 | 199 | |
michael@0 | 200 | /** |
michael@0 | 201 | * Notify the hang monitor of current thread wait. |
michael@0 | 202 | * Call this method before entering a wait state; call |
michael@0 | 203 | * NotifyActivity when subsequently exiting the wait state. |
michael@0 | 204 | */ |
michael@0 | 205 | void NotifyWait(); |
michael@0 | 206 | }; |
michael@0 | 207 | |
michael@0 | 208 | } // namespace mozilla |
michael@0 | 209 | |
michael@0 | 210 | #endif // mozilla_BackgroundHangMonitor_h |