1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef MEDIA_AUDIO_AUDIO_THREAD_HANG_MONITOR_H_
6 #define MEDIA_AUDIO_AUDIO_THREAD_HANG_MONITOR_H_
7 
8 #include "media/audio/audio_manager.h"
9 
10 #include <atomic>
11 #include <memory>
12 
13 #include "base/callback_forward.h"
14 #include "base/gtest_prod_util.h"
15 #include "base/macros.h"
16 #include "base/memory/ref_counted.h"
17 #include "base/memory/scoped_refptr.h"
18 #include "base/optional.h"
19 #include "base/sequence_checker.h"
20 #include "base/sequenced_task_runner.h"
21 #include "base/time/time.h"
22 #include "base/timer/timer.h"
23 #include "media/base/media_export.h"
24 
25 namespace base {
26 class TickClock;
27 class SingleThreadTaskRunner;
28 }  // namespace base
29 
30 namespace media {
31 
32 // This class detects if the audio manager thread is hung. It logs a histogram,
33 // and can optionally (if |dump_on_hang| is set) upload a crash dump when a hang
34 // is detected. It runs on a task runner from the task scheduler. It works by
35 // posting a task to the audio thread every minute and checking that it was
36 // executed. If three consecutive such pings are missed, the thread is
37 // considered hung.
38 class MEDIA_EXPORT AudioThreadHangMonitor final {
39  public:
40   using Ptr =
41       std::unique_ptr<AudioThreadHangMonitor, base::OnTaskRunnerDeleter>;
42 
43   // These values are histogrammed over time; do not change their ordinal
44   // values.
45   enum class ThreadStatus {
46     // kNone = 0, obsolete.
47     kStarted = 1,
48     kHung,
49     kRecovered,
50     kMaxValue = kRecovered
51   };
52 
53   enum class HangAction {
54     // Do nothing. (UMA logging is always done.)
55     kDoNothing,
56     // A crash dump will be collected the first time the thread is detected as
57     // hung (note that no actual crashing is involved).
58     kDump,
59     // Terminate the current process with exit code 0.
60     kTerminateCurrentProcess,
61     // Terminate the current process with exit code 1, which yields a crash
62     // dump.
63     kDumpAndTerminateCurrentProcess
64   };
65 
66   // |monitor_task_runner| may be set explicitly by tests only. Other callers
67   // should use the default. If |hang_deadline| is not provided, or if it's
68   // zero, a default value is used.
69   static Ptr Create(
70       HangAction hang_action,
71       base::Optional<base::TimeDelta> hang_deadline,
72       const base::TickClock* clock,
73       scoped_refptr<base::SingleThreadTaskRunner> audio_thread_task_runner,
74       scoped_refptr<base::SequencedTaskRunner> monitor_task_runner = nullptr);
75 
76   ~AudioThreadHangMonitor();
77 
78   // Thread-safe.
79   bool IsAudioThreadHung() const;
80 
81  private:
82   friend class AudioThreadHangMonitorTest;
83 
84   class SharedAtomicFlag final
85       : public base::RefCountedThreadSafe<SharedAtomicFlag> {
86    public:
87     SharedAtomicFlag();
88 
89     std::atomic_bool flag_ = {false};
90 
91    private:
92     friend class base::RefCountedThreadSafe<SharedAtomicFlag>;
93     ~SharedAtomicFlag();
94   };
95 
96   AudioThreadHangMonitor(
97       HangAction hang_action,
98       base::Optional<base::TimeDelta> hang_deadline,
99       const base::TickClock* clock,
100       scoped_refptr<base::SingleThreadTaskRunner> audio_thread_task_runner);
101 
102   void StartTimer();
103 
104   bool NeverLoggedThreadHung() const;
105   bool NeverLoggedThreadRecoveredAfterHung() const;
106 
107   // This function is run by the |timer_|. It checks if the audio thread has
108   // shown signs of life since the last time it was called (by checking the
109   // |alive_flag_|) and updates the value of |successful_pings_| and
110   // |failed_pings_| as appropriate. It also changes the thread status and logs
111   // its value to a histogram.
112   void CheckIfAudioThreadIsAlive();
113 
114   // LogHistogramThreadStatus logs |thread_status_| to a histogram.
115   void LogHistogramThreadStatus();
116 
117   // For tests. See below functions.
118   void SetHangActionCallbacksForTesting(
119       base::RepeatingClosure dump_callback,
120       base::RepeatingClosure terminate_process_callback);
121 
122   // Thin wrapper functions that either executes the default or runs a callback
123   // set with SetHangActioncallbacksForTesting(), for testing purposes.
124   void DumpWithoutCrashing();
125   void TerminateCurrentProcess();
126 
127   const base::TickClock* const clock_;
128 
129   // This flag is set to false on the monitor sequence and then set to true on
130   // the audio thread to indicate that the audio thread is alive.
131   const scoped_refptr<SharedAtomicFlag> alive_flag_;
132 
133   // |audio_task_runner_| is the task runner of the audio thread.
134   const scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner_;
135 
136   // Which action(s) to take when detected hung thread.
137   const HangAction hang_action_;
138 
139   // At which interval to ping and see if the thread is running.
140   const base::TimeDelta ping_interval_;
141 
142   // For testing. See DumpWithoutCrashing() and TerminateCurrentProcess().
143   base::RepeatingClosure dump_callback_;
144   base::RepeatingClosure terminate_process_callback_;
145 
146   std::atomic<ThreadStatus> audio_thread_status_ = {ThreadStatus::kStarted};
147 
148   // All fields below are accessed on |monitor_sequence|.
149   SEQUENCE_CHECKER(monitor_sequence_);
150 
151   // Timer to check |alive_flag_| regularly.
152   base::RepeatingTimer timer_;
153 
154   // This variable is used to check to detect suspend/resume cycles.
155   // If a long time has passed since the timer was last fired, it is likely due
156   // to the machine being suspended. In such a case, we want to avoid falsely
157   // detecting the audio thread as hung.
158   base::TimeTicks last_check_time_ = base::TimeTicks();
159 
160   // |recent_ping_state_| tracks the recent life signs from the audio thread. If
161   // the most recent ping was successful, the number indicates the number of
162   // successive successful pings. If the most recent ping was failed, the number
163   // is the negative of the number of successive failed pings.
164   int recent_ping_state_ = 0;
165 
166   DISALLOW_COPY_AND_ASSIGN(AudioThreadHangMonitor);
167 };
168 
169 }  // namespace media
170 
171 #endif  // MEDIA_AUDIO_AUDIO_THREAD_HANG_MONITOR_H_
172