blob: c07b5ff47ee6e041edfa97dd427b288fb5f2fdf1 [file] [log] [blame]
// Copyright 2017 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/midi/task_service.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/stringprintf.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
namespace midi {
namespace {
constexpr TaskService::InstanceId kInvalidInstanceId = -1;
} // namespace
TaskService::TaskService()
: no_tasks_in_flight_cv_(&tasks_in_flight_lock_),
tasks_in_flight_(0),
next_instance_id_(0),
bound_instance_id_(kInvalidInstanceId) {
DETACH_FROM_SEQUENCE(instance_binding_sequence_checker_);
}
TaskService::~TaskService() {
std::vector<std::unique_ptr<base::Thread>> threads;
{
base::AutoLock lock(lock_);
threads = std::move(threads_);
DCHECK_EQ(kInvalidInstanceId, bound_instance_id_);
}
// Should not have any lock to perform thread joins on thread destruction.
// All posted tasks should run before quitting the thread message loop.
threads.clear();
}
bool TaskService::BindInstance() {
DCHECK_CALLED_ON_VALID_SEQUENCE(instance_binding_sequence_checker_);
base::AutoLock lock(lock_);
if (bound_instance_id_ != kInvalidInstanceId)
return false;
bound_instance_id_ = next_instance_id_++;
DCHECK(!default_task_runner_);
default_task_runner_ = base::ThreadTaskRunnerHandle::Get();
return true;
}
bool TaskService::UnbindInstance() {
DCHECK_CALLED_ON_VALID_SEQUENCE(instance_binding_sequence_checker_);
{
base::AutoLock lock(lock_);
if (bound_instance_id_ == kInvalidInstanceId)
return false;
bound_instance_id_ = kInvalidInstanceId;
DCHECK(default_task_runner_);
default_task_runner_ = nullptr;
}
// From now on RunTask will never run any task bound to the instance id.
// But invoked tasks might be still running here. To ensure no task runs on
// quitting this method, wait for all tasks to complete.
base::AutoLock tasks_in_flight_auto_lock(tasks_in_flight_lock_);
while (tasks_in_flight_ > 0)
no_tasks_in_flight_cv_.Wait();
return true;
}
bool TaskService::IsOnTaskRunner(RunnerId runner_id) {
base::AutoLock lock(lock_);
if (bound_instance_id_ == kInvalidInstanceId)
return false;
if (runner_id == kDefaultRunnerId)
return default_task_runner_->BelongsToCurrentThread();
size_t thread = runner_id - 1;
if (threads_.size() <= thread || !threads_[thread])
return false;
return threads_[thread]->task_runner()->BelongsToCurrentThread();
}
void TaskService::PostStaticTask(RunnerId runner_id, base::OnceClosure task) {
{
// Disallow to post a task when no instance is bound, so that new threads
// can not be created after the thread finalization in the destructor.
base::AutoLock lock(lock_);
if (bound_instance_id_ == kInvalidInstanceId)
return;
}
scoped_refptr<base::SingleThreadTaskRunner> runner;
GetTaskRunner(runner_id)->PostTask(FROM_HERE, std::move(task));
}
void TaskService::PostBoundTask(RunnerId runner_id, base::OnceClosure task) {
InstanceId instance_id;
{
base::AutoLock lock(lock_);
if (bound_instance_id_ == kInvalidInstanceId)
return;
instance_id = bound_instance_id_;
}
GetTaskRunner(runner_id)->PostTask(
FROM_HERE, base::BindOnce(&TaskService::RunTask, base::Unretained(this),
instance_id, runner_id, std::move(task)));
}
void TaskService::PostBoundDelayedTask(RunnerId runner_id,
base::OnceClosure task,
base::TimeDelta delay) {
InstanceId instance_id;
{
base::AutoLock lock(lock_);
if (bound_instance_id_ == kInvalidInstanceId)
return;
instance_id = bound_instance_id_;
}
GetTaskRunner(runner_id)->PostDelayedTask(
FROM_HERE,
base::BindOnce(&TaskService::RunTask, base::Unretained(this), instance_id,
runner_id, std::move(task)),
delay);
}
scoped_refptr<base::SingleThreadTaskRunner> TaskService::GetTaskRunner(
RunnerId runner_id) {
base::AutoLock lock(lock_);
if (runner_id == kDefaultRunnerId)
return default_task_runner_;
if (threads_.size() < runner_id)
threads_.resize(runner_id);
size_t thread = runner_id - 1;
if (!threads_[thread]) {
threads_[thread] = base::MakeUnique<base::Thread>(
base::StringPrintf("MidiService_TaskService_Thread(%zu)", runner_id));
base::Thread::Options options;
#if defined(OS_WIN)
threads_[thread]->init_com_with_mta(true);
#elif defined(OS_MACOSX)
options.message_loop_type = base::MessageLoop::TYPE_UI;
#endif
threads_[thread]->StartWithOptions(options);
}
return threads_[thread]->task_runner();
}
void TaskService::RunTask(InstanceId instance_id,
RunnerId runner_id,
base::OnceClosure task) {
{
base::AutoLock tasks_in_flight_auto_lock(tasks_in_flight_lock_);
++tasks_in_flight_;
}
if (IsInstanceIdStillBound(instance_id))
std::move(task).Run();
{
base::AutoLock tasks_in_flight_auto_lock(tasks_in_flight_lock_);
--tasks_in_flight_;
DCHECK_GE(tasks_in_flight_, 0);
if (tasks_in_flight_ == 0)
no_tasks_in_flight_cv_.Signal();
}
}
bool TaskService::IsInstanceIdStillBound(InstanceId instance_id) {
base::AutoLock lock(lock_);
return instance_id == bound_instance_id_;
}
} // namespace midi