blob: 363dd69dd42b939101228114279c558a34d29a13 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/audio/audio_manager.h"
#include <atomic>
#include <memory>
#include "base/callback_forward.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/optional.h"
#include "base/sequence_checker.h"
#include "base/sequenced_task_runner.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "media/base/media_export.h"
#include "media/base/media_switches.h"
namespace base {
class TickClock;
class SingleThreadTaskRunner;
} // namespace base
namespace media {
// This class detects if the audio manager thread is hung. It logs a histogram,
// and can optionally (if |dump_on_hang| is set) upload a crash dump when a hang
// is detected. It runs on a task runner from the task scheduler. It works by
// posting a task to the audio thread every minute and checking that it was
// executed. If three consecutive such pings are missed, the thread is
// considered hung.
class MEDIA_EXPORT AudioThreadHangMonitor final {
using Ptr =
std::unique_ptr<AudioThreadHangMonitor, base::OnTaskRunnerDeleter>;
// These values are histogrammed over time; do not change their ordinal
// values.
enum class ThreadStatus {
// kNone = 0, obsolete.
kStarted = 1,
kMaxValue = kRecovered
enum class HangAction {
// Do nothing. (UMA logging is always done.)
// A crash dump will be collected the first time the thread is detected as
// hung (note that no actual crashing is involved).
// Terminate the current process with exit code 0.
// Terminate the current process with exit code 1, which yields a crash
// dump.
// |monitor_task_runner| may be set explicitly by tests only. Other callers
// should use the default. If |hang_deadline| is not provided, or if it's
// zero, a default value is used.
static Ptr Create(
HangAction hang_action,
base::Optional<base::TimeDelta> hang_deadline,
const base::TickClock* clock,
scoped_refptr<base::SingleThreadTaskRunner> audio_thread_task_runner,
scoped_refptr<base::SequencedTaskRunner> monitor_task_runner = nullptr);
// Thread-safe.
bool IsAudioThreadHung() const;
friend class AudioThreadHangMonitorTest;
class SharedAtomicFlag final
: public base::RefCountedThreadSafe<SharedAtomicFlag> {
std::atomic_bool flag_ = {false};
friend class base::RefCountedThreadSafe<SharedAtomicFlag>;
HangAction hang_action,
base::Optional<base::TimeDelta> hang_deadline,
const base::TickClock* clock,
scoped_refptr<base::SingleThreadTaskRunner> audio_thread_task_runner);
void StartTimer();
bool NeverLoggedThreadHung() const;
bool NeverLoggedThreadRecoveredAfterHung() const;
// This function is run by the |timer_|. It checks if the audio thread has
// shown signs of life since the last time it was called (by checking the
// |alive_flag_|) and updates the value of |successful_pings_| and
// |failed_pings_| as appropriate. It also changes the thread status and logs
// its value to a histogram.
void CheckIfAudioThreadIsAlive();
// LogHistogramThreadStatus logs |thread_status_| to a histogram.
void LogHistogramThreadStatus();
// For tests. See below functions.
void SetHangActionCallbacksForTesting(
base::RepeatingClosure dump_callback,
base::RepeatingClosure terminate_process_callback);
// Thin wrapper functions that either executes the default or runs a callback
// set with SetHangActioncallbacksForTesting(), for testing purposes.
void DumpWithoutCrashing();
void TerminateCurrentProcess();
const base::TickClock* const clock_;
// This flag is set to false on the monitor sequence and then set to true on
// the audio thread to indicate that the audio thread is alive.
const scoped_refptr<SharedAtomicFlag> alive_flag_;
// |audio_task_runner_| is the task runner of the audio thread.
const scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner_;
// Which action(s) to take when detected hung thread.
const HangAction hang_action_;
// At which interval to ping and see if the thread is running.
const base::TimeDelta ping_interval_;
// For testing. See DumpWithoutCrashing() and TerminateCurrentProcess().
base::RepeatingClosure dump_callback_;
base::RepeatingClosure terminate_process_callback_;
std::atomic<ThreadStatus> audio_thread_status_ = {ThreadStatus::kStarted};
// All fields below are accessed on |monitor_sequence|.
// Timer to check |alive_flag_| regularly.
base::RepeatingTimer timer_;
// This variable is used to check to detect suspend/resume cycles.
// If a long time has passed since the timer was last fired, it is likely due
// to the machine being suspended. In such a case, we want to avoid falsely
// detecting the audio thread as hung.
base::TimeTicks last_check_time_ = base::TimeTicks();
// |recent_ping_state_| tracks the recent life signs from the audio thread. If
// the most recent ping was successful, the number indicates the number of
// successive successful pings. If the most recent ping was failed, the number
// is the negative of the number of successive failed pings.
int recent_ping_state_ = 0;
} // namespace media