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

     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/. */
     6 #ifndef mozilla_BackgroundHangMonitor_h
     7 #define mozilla_BackgroundHangMonitor_h
     9 #include "mozilla/RefPtr.h"
    10 #include "mozilla/Monitor.h"
    12 #include <stdint.h>
    14 namespace mozilla {
    16 namespace Telemetry {
    17 class ThreadHangStats;
    18 };
    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
    27 class BackgroundHangThread;
    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;
   115 public:
   116   static const uint32_t kNoTimeout = 0;
   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;
   139     ThreadHangStatsIterator(const ThreadHangStatsIterator&);
   140     ThreadHangStatsIterator& operator=(const ThreadHangStatsIterator&);
   142   public:
   143     /**
   144      * Create an ThreadHangStatsIterator instance and take the internal lock.
   145      * Internal lock is released on destruction.
   146      */
   147     ThreadHangStatsIterator();
   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   };
   156   /**
   157    * Enable hang monitoring.
   158    * Must return before using BackgroundHangMonitor.
   159    */
   160   static void Startup();
   162   /**
   163    * Disable hang monitoring.
   164    * Can be called without destroying all BackgroundHangMonitors first.
   165    */
   166   static void Shutdown();
   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);
   181   /**
   182    * Monitor hangs using an existing monitor
   183    * associated with the current thread.
   184    */
   185   BackgroundHangMonitor();
   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();
   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();
   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 };
   208 } // namespace mozilla
   210 #endif // mozilla_BackgroundHangMonitor_h

mercurial