xpcom/threads/BackgroundHangMonitor.h

changeset 0
6474c204b198
     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

mercurial