blob: a483820d84d744b7184c88d470e969d39f35078a [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 "services/audio/owning_audio_manager_accessor.h"
#include <memory>
#include <utility>
#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"
#include "media/audio/audio_manager.h"
#include "media/audio/audio_thread.h"
#include "media/audio/audio_thread_hang_monitor.h"
using HangAction = media::AudioThreadHangMonitor::HangAction;
namespace audio {
namespace {
absl::optional<base::TimeDelta> GetAudioThreadHangDeadline() {
if (!base::FeatureList::IsEnabled(
features::kAudioServiceOutOfProcessKillAtHang)) {
return absl::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 absl::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.
class MainThread final : public media::AudioThread {
public:
MainThread();
~MainThread() final;
// AudioThread implementation.
void Stop() final;
bool IsHung() const final;
base::SingleThreadTaskRunner* GetTaskRunner() final;
base::SingleThreadTaskRunner* GetWorkerTaskRunner() final;
private:
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
// This is not started until the first time GetWorkerTaskRunner() is called.
base::Thread worker_thread_;
scoped_refptr<base::SingleThreadTaskRunner> worker_task_runner_;
media::AudioThreadHangMonitor::Ptr hang_monitor_;
DISALLOW_COPY_AND_ASSIGN(MainThread);
};
MainThread::MainThread()
: task_runner_(base::ThreadTaskRunnerHandle::Get()),
worker_thread_("AudioWorkerThread"),
hang_monitor_(media::AudioThreadHangMonitor::Create(
GetAudioThreadHangAction(),
GetAudioThreadHangDeadline(),
base::DefaultTickClock::GetInstance(),
task_runner_)) {}
MainThread::~MainThread() {
DCHECK(task_runner_->BelongsToCurrentThread());
}
void MainThread::Stop() {
DCHECK(task_runner_->BelongsToCurrentThread());
hang_monitor_.reset();
if (worker_task_runner_) {
worker_task_runner_ = nullptr;
worker_thread_.Stop();
}
}
bool MainThread::IsHung() const {
return hang_monitor_->IsAudioThreadHung();
}
base::SingleThreadTaskRunner* MainThread::GetTaskRunner() {
return task_runner_.get();
}
base::SingleThreadTaskRunner* MainThread::GetWorkerTaskRunner() {
DCHECK(
task_runner_->BelongsToCurrentThread() ||
(worker_task_runner_ && worker_task_runner_->BelongsToCurrentThread()));
if (!worker_task_runner_) {
base::Thread::Options options;
options.timer_slack = base::TIMER_SLACK_NONE;
options.priority = base::ThreadPriority::REALTIME_AUDIO;
CHECK(worker_thread_.StartWithOptions(std::move(options)));
worker_task_runner_ = worker_thread_.task_runner();
}
return worker_task_runner_.get();
}
} // namespace
OwningAudioManagerAccessor::OwningAudioManagerAccessor(
AudioManagerFactoryCallback audio_manager_factory_cb)
: audio_manager_factory_cb_(std::move(audio_manager_factory_cb)) {
DCHECK(audio_manager_factory_cb_);
}
OwningAudioManagerAccessor::~OwningAudioManagerAccessor() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
}
media::AudioManager* OwningAudioManagerAccessor::GetAudioManager() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!audio_manager_) {
TRACE_EVENT0("audio", "AudioManager creation");
DCHECK(audio_manager_factory_cb_);
DCHECK(log_factory_);
base::TimeTicks creation_start_time = base::TimeTicks::Now();
audio_manager_ = std::move(audio_manager_factory_cb_)
.Run(std::make_unique<MainThread>(), log_factory_);
DCHECK(audio_manager_);
UMA_HISTOGRAM_TIMES("Media.AudioService.AudioManagerStartupTime",
base::TimeTicks::Now() - creation_start_time);
}
DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
return audio_manager_.get();
}
void OwningAudioManagerAccessor::SetAudioLogFactory(
media::AudioLogFactory* log_factory) {
log_factory_ = log_factory;
}
void OwningAudioManagerAccessor::Shutdown() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (audio_manager_)
audio_manager_->Shutdown();
audio_manager_factory_cb_ = AudioManagerFactoryCallback();
}
} // namespace audio