Kill audio process at audio thread hang.
* Introduce feature AudioServiceOutOfProcessKillAtHang with timeout parameter.
* If feature is enabled, kill process when audio thread is detected
as hung, using timeout (or default timeout).
* Add info about feature and parameter in media-internals.
Enable with command line flags
--enable-features=AudioServiceAudioStreams,AudioServiceOutOfProcess,"AudioServiceOutOfProcessKillAtHang<MyStudyName" --force-fieldtrials=MyStudyName/Group1 --force-fieldtrial-params=MyStudyName.Group1:timeout_seconds/60
Bug: 963810
Change-Id: Ic05a552763452855982ae76dc92c4b04872b0220
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1615018
Reviewed-by: Olga Sharonova <olka@chromium.org>
Reviewed-by: Max Morin <maxmorin@chromium.org>
Commit-Queue: Henrik Grunell <grunell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#661404}
diff --git a/content/browser/media/media_internals.cc b/content/browser/media/media_internals.cc
index 344aa3a..d1a17e07 100644
--- a/content/browser/media/media_internals.cc
+++ b/content/browser/media/media_internals.cc
@@ -12,9 +12,11 @@
#include "base/bind.h"
#include "base/containers/adapters.h"
#include "base/feature_list.h"
+#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/stl_util.h"
+#include "base/strings/strcat.h"
#include "base/strings/string16.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
@@ -33,6 +35,7 @@
#include "content/public/browser/web_ui.h"
#include "content/public/common/content_features.h"
#include "content/public/common/service_manager_connection.h"
+#include "media/audio/audio_features.h"
#include "media/base/audio_parameters.h"
#include "media/base/media_log_event.h"
#include "media/filters/gpu_video_decoder.h"
@@ -705,8 +708,25 @@
base::Value(base::FeatureList::IsEnabled(feature) ? "Enabled"
: "Disabled"));
};
+
set_feature_data(features::kAudioServiceAudioStreams);
set_feature_data(features::kAudioServiceOutOfProcess);
+
+ std::string feature_value_string;
+ if (base::FeatureList::IsEnabled(
+ features::kAudioServiceOutOfProcessKillAtHang)) {
+ std::string timeout_value = base::GetFieldTrialParamValueByFeature(
+ features::kAudioServiceOutOfProcessKillAtHang, "timeout_seconds");
+ if (timeout_value.empty())
+ timeout_value = "<undefined>";
+ feature_value_string =
+ base::StrCat({"Enabled, timeout = ", timeout_value, " seconds"});
+ } else {
+ feature_value_string = "Disabled";
+ }
+ audio_info_data.SetKey(features::kAudioServiceOutOfProcessKillAtHang.name,
+ base::Value(feature_value_string));
+
set_feature_data(features::kAudioServiceLaunchOnStartup);
set_feature_data(service_manager::features::kAudioServiceSandbox);
set_feature_data(features::kWebRtcApmInAudioService);
diff --git a/media/audio/audio_features.cc b/media/audio/audio_features.cc
index cfb0b53..e5fdc299 100644
--- a/media/audio/audio_features.cc
+++ b/media/audio/audio_features.cc
@@ -6,6 +6,11 @@
namespace features {
+// When the audio service in a separate process, kill it when a hang is
+// detected. It will be restarted when needed.
+const base::Feature kAudioServiceOutOfProcessKillAtHang{
+ "AudioServiceOutOfProcessKillAtHang", base::FEATURE_DISABLED_BY_DEFAULT};
+
// If enabled, base::DumpWithoutCrashing is called whenever an audio service
// hang is detected.
const base::Feature kDumpOnAudioServiceHang{"DumpOnAudioServiceHang",
diff --git a/media/audio/audio_features.h b/media/audio/audio_features.h
index abf4c66..70e228d 100644
--- a/media/audio/audio_features.h
+++ b/media/audio/audio_features.h
@@ -11,6 +11,7 @@
namespace features {
+MEDIA_EXPORT extern const base::Feature kAudioServiceOutOfProcessKillAtHang;
MEDIA_EXPORT extern const base::Feature kDumpOnAudioServiceHang;
#if defined(OS_CHROMEOS)
diff --git a/media/audio/audio_thread_hang_monitor.cc b/media/audio/audio_thread_hang_monitor.cc
index 7e94838..727e198 100644
--- a/media/audio/audio_thread_hang_monitor.cc
+++ b/media/audio/audio_thread_hang_monitor.cc
@@ -8,10 +8,12 @@
#include <utility>
#include "base/bind.h"
+#include "base/callback.h"
#include "base/debug/dump_without_crashing.h"
#include "base/location.h"
#include "base/metrics/histogram_macros.h"
#include "base/power_monitor/power_monitor.h"
+#include "base/process/process.h"
#include "base/single_thread_task_runner.h"
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
@@ -28,7 +30,9 @@
// caught (e.g., the system suspends before a OnSuspend() event can be fired).
constexpr int kMaxFailedPingsCount = 3;
-constexpr base::TimeDelta kHungDeadline = base::TimeDelta::FromMinutes(1);
+// The default deadline after which we consider the audio thread hung.
+constexpr base::TimeDelta kDefaultHangDeadline =
+ base::TimeDelta::FromMinutes(3);
} // namespace
@@ -37,7 +41,8 @@
// static
AudioThreadHangMonitor::Ptr AudioThreadHangMonitor::Create(
- bool dump_on_hang,
+ 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) {
@@ -45,7 +50,7 @@
monitor_task_runner = base::CreateSequencedTaskRunnerWithTraits({});
auto monitor =
- Ptr(new AudioThreadHangMonitor(dump_on_hang, clock,
+ Ptr(new AudioThreadHangMonitor(hang_action, hang_deadline, clock,
std::move(audio_thread_task_runner)),
base::OnTaskRunnerDeleter(monitor_task_runner));
@@ -65,13 +70,19 @@
}
AudioThreadHangMonitor::AudioThreadHangMonitor(
- bool dump_on_hang,
+ HangAction hang_action,
+ base::Optional<base::TimeDelta> hang_deadline,
const base::TickClock* clock,
scoped_refptr<base::SingleThreadTaskRunner> audio_thread_task_runner)
: clock_(clock),
alive_flag_(base::MakeRefCounted<SharedAtomicFlag>()),
audio_task_runner_(std::move(audio_thread_task_runner)),
- dump_on_hang_(dump_on_hang),
+ hang_action_(hang_action),
+ ping_interval_((hang_deadline ? hang_deadline.value().is_zero()
+ ? kDefaultHangDeadline
+ : hang_deadline.value()
+ : kDefaultHangDeadline) /
+ kMaxFailedPingsCount),
timer_(clock_) {
DETACH_FROM_SEQUENCE(monitor_sequence_);
}
@@ -88,7 +99,7 @@
// |this| owns |timer_|, so Unretained is safe.
timer_.Start(
- FROM_HERE, kHungDeadline,
+ FROM_HERE, ping_interval_,
base::BindRepeating(&AudioThreadHangMonitor::CheckIfAudioThreadIsAlive,
base::Unretained(this)));
}
@@ -112,7 +123,7 @@
// An unexpected |time_since_last_check| may indicate that the system has been
// in sleep mode, in which case the audio thread may have had insufficient
// time to respond to the ping. In such a case, skip the check for now.
- if (time_since_last_check > kHungDeadline + base::TimeDelta::FromSeconds(1))
+ if (time_since_last_check > ping_interval_ + base::TimeDelta::FromSeconds(1))
return;
const bool audio_thread_responded_to_last_ping = alive_flag_->flag_;
@@ -138,8 +149,13 @@
audio_thread_status_ = ThreadStatus::kHung;
LogHistogramThreadStatus();
- if (dump_on_hang_) {
- base::debug::DumpWithoutCrashing();
+ if (hang_action_ == HangAction::kDump ||
+ hang_action_ == HangAction::kDumpAndTerminateCurrentProcess) {
+ DumpWithoutCrashing();
+ }
+ if (hang_action_ == HangAction::kTerminateCurrentProcess ||
+ hang_action_ == HangAction::kDumpAndTerminateCurrentProcess) {
+ TerminateCurrentProcess();
}
}
}
@@ -158,4 +174,25 @@
audio_thread_status_.load());
}
+void AudioThreadHangMonitor::SetHangActionCallbacksForTesting(
+ base::RepeatingClosure dump_callback,
+ base::RepeatingClosure terminate_process_callback) {
+ dump_callback_ = std::move(dump_callback);
+ terminate_process_callback_ = std::move(terminate_process_callback);
+}
+
+void AudioThreadHangMonitor::DumpWithoutCrashing() {
+ if (!dump_callback_.is_null())
+ dump_callback_.Run();
+ else
+ base::debug::DumpWithoutCrashing();
+}
+
+void AudioThreadHangMonitor::TerminateCurrentProcess() {
+ if (!terminate_process_callback_.is_null())
+ terminate_process_callback_.Run();
+ else
+ base::Process::TerminateCurrentProcessImmediately(1);
+}
+
} // namespace media
diff --git a/media/audio/audio_thread_hang_monitor.h b/media/audio/audio_thread_hang_monitor.h
index bcd9d51..363dd69 100644
--- a/media/audio/audio_thread_hang_monitor.h
+++ b/media/audio/audio_thread_hang_monitor.h
@@ -10,9 +10,12 @@
#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"
@@ -48,10 +51,25 @@
kMaxValue = kRecovered
};
+ enum class HangAction {
+ // Do nothing. (UMA logging is always done.)
+ kDoNothing,
+ // A crash dump will be collected the first time the thread is detected as
+ // hung (note that no actual crashing is involved).
+ kDump,
+ // Terminate the current process with exit code 0.
+ kTerminateCurrentProcess,
+ // Terminate the current process with exit code 1, which yields a crash
+ // dump.
+ kDumpAndTerminateCurrentProcess
+ };
+
// |monitor_task_runner| may be set explicitly by tests only. Other callers
- // should use the default.
+ // should use the default. If |hang_deadline| is not provided, or if it's
+ // zero, a default value is used.
static Ptr Create(
- bool dump_on_hang,
+ 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);
@@ -62,6 +80,8 @@
bool IsAudioThreadHung() const;
private:
+ friend class AudioThreadHangMonitorTest;
+
class SharedAtomicFlag final
: public base::RefCountedThreadSafe<SharedAtomicFlag> {
public:
@@ -75,7 +95,8 @@
};
AudioThreadHangMonitor(
- bool dump_on_hang,
+ HangAction hang_action,
+ base::Optional<base::TimeDelta> hang_deadline,
const base::TickClock* clock,
scoped_refptr<base::SingleThreadTaskRunner> audio_thread_task_runner);
@@ -94,6 +115,16 @@
// 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
@@ -103,9 +134,15 @@
// |audio_task_runner_| is the task runner of the audio thread.
const scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner_;
- // If |dump_on_hang_| is set, a crash dump will be collected the first time
- // the thread is detected as hung (note that no actual crashing is involved).
- const bool dump_on_hang_;
+ // 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};
diff --git a/media/audio/audio_thread_hang_monitor_unittest.cc b/media/audio/audio_thread_hang_monitor_unittest.cc
index c90d4f6..364edb7 100644
--- a/media/audio/audio_thread_hang_monitor_unittest.cc
+++ b/media/audio/audio_thread_hang_monitor_unittest.cc
@@ -6,6 +6,7 @@
#include "base/bind.h"
#include "base/location.h"
+#include "base/optional.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/post_task.h"
#include "base/test/metrics/histogram_tester.h"
@@ -16,6 +17,7 @@
using testing::ElementsAre;
using testing::Test;
+using HangAction = media::AudioThreadHangMonitor::HangAction;
namespace media {
@@ -28,6 +30,9 @@
constexpr int kRecovered =
static_cast<int>(AudioThreadHangMonitor::ThreadStatus::kRecovered);
+constexpr base::TimeDelta kShortHangDeadline = base::TimeDelta::FromSeconds(5);
+constexpr base::TimeDelta kLongHangDeadline = base::TimeDelta::FromMinutes(30);
+
} // namespace
class AudioThreadHangMonitorTest : public Test {
@@ -42,8 +47,8 @@
// runner since ScopedTaskEnvironment::FastForwardBy only works for the main
// thread.
hang_monitor_ = AudioThreadHangMonitor::Create(
- false, task_env_.GetMockTickClock(), audio_thread_.task_runner(),
- task_env_.GetMainThreadTaskRunner());
+ HangAction::kDoNothing, base::nullopt, task_env_.GetMockTickClock(),
+ audio_thread_.task_runner(), task_env_.GetMainThreadTaskRunner());
}
~AudioThreadHangMonitorTest() override {
@@ -51,6 +56,14 @@
task_env_.RunUntilIdle();
}
+ void SetHangActionCallbacksForTesting() {
+ hang_monitor_->SetHangActionCallbacksForTesting(
+ base::BindRepeating(&AudioThreadHangMonitorTest::HangActionDump,
+ base::Unretained(this)),
+ base::BindRepeating(&AudioThreadHangMonitorTest::HangActionTerminate,
+ base::Unretained(this)));
+ }
+
void RunUntilIdle() { task_env_.RunUntilIdle(); }
void FlushAudioThread() {
@@ -70,6 +83,9 @@
base::BindOnce(&base::WaitableEvent::Wait, base::Unretained(&event_)));
}
+ MOCK_METHOD0(HangActionDump, void());
+ MOCK_METHOD0(HangActionTerminate, void());
+
base::WaitableEvent event_;
base::test::ScopedTaskEnvironment task_env_;
base::HistogramTester histograms_;
@@ -109,6 +125,95 @@
ElementsAre(base::Bucket(kStarted, 1), base::Bucket(kHung, 1)));
}
+TEST_F(AudioThreadHangMonitorTest, DoesNotLogThreadHungWithShortDeadline) {
+ hang_monitor_ = AudioThreadHangMonitor::Create(
+ HangAction::kDoNothing, kShortHangDeadline, task_env_.GetMockTickClock(),
+ audio_thread_.task_runner(), task_env_.GetMainThreadTaskRunner());
+ RunUntilIdle();
+
+ BlockAudioThreadUntilEvent();
+ task_env_.FastForwardBy(kShortHangDeadline / 2);
+ event_.Signal();
+
+ // Two started events, one for the originally created hang monitor and one for
+ // the new created here.
+ EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+ ElementsAre(base::Bucket(kStarted, 2)));
+}
+
+TEST_F(AudioThreadHangMonitorTest, LogsThreadHungWithShortDeadline) {
+ hang_monitor_ = AudioThreadHangMonitor::Create(
+ HangAction::kDoNothing, kShortHangDeadline, task_env_.GetMockTickClock(),
+ audio_thread_.task_runner(), task_env_.GetMainThreadTaskRunner());
+ RunUntilIdle();
+
+ BlockAudioThreadUntilEvent();
+ task_env_.FastForwardBy(kShortHangDeadline * 2);
+ event_.Signal();
+
+ // Two started events, one for the originally created hang monitor and one for
+ // the new created here.
+ EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+ ElementsAre(base::Bucket(kStarted, 2), base::Bucket(kHung, 1)));
+}
+
+TEST_F(AudioThreadHangMonitorTest, DoesNotLogThreadHungWithLongDeadline) {
+ hang_monitor_ = AudioThreadHangMonitor::Create(
+ HangAction::kDoNothing, kLongHangDeadline, task_env_.GetMockTickClock(),
+ audio_thread_.task_runner(), task_env_.GetMainThreadTaskRunner());
+ RunUntilIdle();
+
+ BlockAudioThreadUntilEvent();
+ task_env_.FastForwardBy(kLongHangDeadline / 2);
+ event_.Signal();
+
+ // Two started events, one for the originally created hang monitor and one for
+ // the new created here.
+ EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+ ElementsAre(base::Bucket(kStarted, 2)));
+}
+
+TEST_F(AudioThreadHangMonitorTest, LogsThreadHungWithLongDeadline) {
+ hang_monitor_ = AudioThreadHangMonitor::Create(
+ HangAction::kDoNothing, kLongHangDeadline, task_env_.GetMockTickClock(),
+ audio_thread_.task_runner(), task_env_.GetMainThreadTaskRunner());
+ RunUntilIdle();
+
+ BlockAudioThreadUntilEvent();
+ task_env_.FastForwardBy(kLongHangDeadline * 2);
+ event_.Signal();
+
+ // Two started events, one for the originally created hang monitor and one for
+ // the new created here.
+ EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+ ElementsAre(base::Bucket(kStarted, 2), base::Bucket(kHung, 1)));
+}
+
+// Zero deadline means that the default deadline should be used.
+TEST_F(AudioThreadHangMonitorTest, ZeroDeadlineMeansDefaultDeadline) {
+ hang_monitor_ = AudioThreadHangMonitor::Create(
+ HangAction::kDoNothing, base::TimeDelta(), task_env_.GetMockTickClock(),
+ audio_thread_.task_runner(), task_env_.GetMainThreadTaskRunner());
+ RunUntilIdle();
+
+ for (int i = 0; i < 10; ++i) {
+ // Flush the audio thread, then advance the clock. The audio thread should
+ // register as "alive" every time.
+ FlushAudioThread();
+ task_env_.FastForwardBy(base::TimeDelta::FromMinutes(1));
+ }
+
+ EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+ ElementsAre(base::Bucket(kStarted, 2)));
+
+ BlockAudioThreadUntilEvent();
+ task_env_.FastForwardBy(base::TimeDelta::FromMinutes(10));
+ event_.Signal();
+
+ EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+ ElementsAre(base::Bucket(kStarted, 2), base::Bucket(kHung, 1)));
+}
+
TEST_F(AudioThreadHangMonitorTest,
LogsRecoveredWhenAudioThreadIsBlockedThenRecovers) {
RunUntilIdle();
@@ -129,4 +234,74 @@
base::Bucket(kRecovered, 1)));
}
+TEST_F(AudioThreadHangMonitorTest, NoHangActionWhenOk) {
+ SetHangActionCallbacksForTesting();
+ RunUntilIdle();
+
+ for (int i = 0; i < 10; ++i) {
+ // Flush the audio thread, then advance the clock. The audio thread should
+ // register as "alive" every time.
+ FlushAudioThread();
+ task_env_.FastForwardBy(base::TimeDelta::FromMinutes(1));
+ }
+
+ EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+ ElementsAre(base::Bucket(kStarted, 1)));
+}
+
+TEST_F(AudioThreadHangMonitorTest, DumpsWhenAudioThreadIsBlocked) {
+ hang_monitor_ = AudioThreadHangMonitor::Create(
+ HangAction::kDump, base::nullopt, task_env_.GetMockTickClock(),
+ audio_thread_.task_runner(), task_env_.GetMainThreadTaskRunner());
+ SetHangActionCallbacksForTesting();
+ RunUntilIdle();
+
+ EXPECT_CALL(*this, HangActionDump).Times(1);
+
+ BlockAudioThreadUntilEvent();
+ task_env_.FastForwardBy(base::TimeDelta::FromMinutes(10));
+ event_.Signal();
+
+ EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+ ElementsAre(base::Bucket(kStarted, 2), base::Bucket(kHung, 1)));
+}
+
+TEST_F(AudioThreadHangMonitorTest, TerminatesProcessWhenAudioThreadIsBlocked) {
+ hang_monitor_ = AudioThreadHangMonitor::Create(
+ HangAction::kTerminateCurrentProcess, base::nullopt,
+ task_env_.GetMockTickClock(), audio_thread_.task_runner(),
+ task_env_.GetMainThreadTaskRunner());
+ SetHangActionCallbacksForTesting();
+ RunUntilIdle();
+
+ EXPECT_CALL(*this, HangActionTerminate).Times(1);
+
+ BlockAudioThreadUntilEvent();
+ task_env_.FastForwardBy(base::TimeDelta::FromMinutes(10));
+ event_.Signal();
+
+ EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+ ElementsAre(base::Bucket(kStarted, 2), base::Bucket(kHung, 1)));
+}
+
+TEST_F(AudioThreadHangMonitorTest,
+ DumpsAndTerminatesProcessWhenAudioThreadIsBlocked) {
+ hang_monitor_ = AudioThreadHangMonitor::Create(
+ HangAction::kDumpAndTerminateCurrentProcess, base::nullopt,
+ task_env_.GetMockTickClock(), audio_thread_.task_runner(),
+ task_env_.GetMainThreadTaskRunner());
+ SetHangActionCallbacksForTesting();
+ RunUntilIdle();
+
+ EXPECT_CALL(*this, HangActionDump).Times(1);
+ EXPECT_CALL(*this, HangActionTerminate).Times(1);
+
+ BlockAudioThreadUntilEvent();
+ task_env_.FastForwardBy(base::TimeDelta::FromMinutes(10));
+ event_.Signal();
+
+ EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+ ElementsAre(base::Bucket(kStarted, 2), base::Bucket(kHung, 1)));
+}
+
} // namespace media
diff --git a/media/audio/audio_thread_impl.cc b/media/audio/audio_thread_impl.cc
index e1bdc11d..c3dfddf 100644
--- a/media/audio/audio_thread_impl.cc
+++ b/media/audio/audio_thread_impl.cc
@@ -4,6 +4,7 @@
#include "media/audio/audio_thread_impl.h"
+#include "base/optional.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/default_tick_clock.h"
#include "build/build_config.h"
@@ -37,7 +38,8 @@
// https://crbug.com/946968: The hang monitor possibly causes crashes on
// Android
hang_monitor_ = AudioThreadHangMonitor::Create(
- false, base::DefaultTickClock::GetInstance(), task_runner_);
+ AudioThreadHangMonitor::HangAction::kDoNothing, base::nullopt,
+ base::DefaultTickClock::GetInstance(), task_runner_);
#endif
}
diff --git a/services/audio/owning_audio_manager_accessor.cc b/services/audio/owning_audio_manager_accessor.cc
index c2998c3..9ecffa35 100644
--- a/services/audio/owning_audio_manager_accessor.cc
+++ b/services/audio/owning_audio_manager_accessor.cc
@@ -9,9 +9,11 @@
#include "base/feature_list.h"
#include "base/macros.h"
+#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_macros.h"
#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"
#include "base/threading/thread.h"
#include "base/time/default_tick_clock.h"
#include "media/audio/audio_features.h"
@@ -19,10 +21,37 @@
#include "media/audio/audio_thread.h"
#include "media/audio/audio_thread_hang_monitor.h"
+using HangAction = media::AudioThreadHangMonitor::HangAction;
+
namespace audio {
namespace {
+base::Optional<base::TimeDelta> GetAudioThreadHangDeadline() {
+ if (!base::FeatureList::IsEnabled(
+ features::kAudioServiceOutOfProcessKillAtHang)) {
+ return base::nullopt;
+ }
+ const std::string timeout_string = base::GetFieldTrialParamValueByFeature(
+ features::kAudioServiceOutOfProcessKillAtHang, "timeout_seconds");
+ int timeout_int = 0;
+ if (!base::StringToInt(timeout_string, &timeout_int) || timeout_int == 0)
+ return base::nullopt;
+ return base::TimeDelta::FromSeconds(timeout_int);
+}
+
+HangAction GetAudioThreadHangAction() {
+ const bool dump =
+ base::FeatureList::IsEnabled(features::kDumpOnAudioServiceHang);
+ const bool kill = base::FeatureList::IsEnabled(
+ features::kAudioServiceOutOfProcessKillAtHang);
+ if (dump) {
+ return kill ? HangAction::kDumpAndTerminateCurrentProcess
+ : HangAction::kDump;
+ }
+ return kill ? HangAction::kTerminateCurrentProcess : HangAction::kDoNothing;
+}
+
// Thread class for hosting owned AudioManager on the main thread of the
// service, with a separate worker thread (started on-demand) for running things
// that shouldn't be blocked by main-thread tasks.
@@ -53,7 +82,8 @@
: task_runner_(base::ThreadTaskRunnerHandle::Get()),
worker_thread_("AudioWorkerThread"),
hang_monitor_(media::AudioThreadHangMonitor::Create(
- base::FeatureList::IsEnabled(features::kDumpOnAudioServiceHang),
+ GetAudioThreadHangAction(),
+ GetAudioThreadHangDeadline(),
base::DefaultTickClock::GetInstance(),
task_runner_)) {}