xpcom/threads/BackgroundHangMonitor.h

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial