xpcom/threads/BackgroundHangMonitor.h

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

mercurial