blob: fc1cfb7fdfc2e858f713480edb83c6638dddc0a7 [file] [log] [blame]
toyoshimccb8ff92017-06-13 05:31:46 -07001// Copyright 2017 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#include "media/midi/task_service.h"
6
7#include "base/strings/stringprintf.h"
8#include "base/threading/thread_task_runner_handle.h"
9
10namespace midi {
11
12namespace {
13
14constexpr TaskService::InstanceId kInvalidInstanceId = -1;
15
16} // namespace
17
18TaskService::TaskService()
Robert Liaof66a9cf2017-08-26 01:40:35 +000019 : no_tasks_in_flight_cv_(&tasks_in_flight_lock_),
20 tasks_in_flight_(0),
21 next_instance_id_(0),
22 bound_instance_id_(kInvalidInstanceId) {}
toyoshimccb8ff92017-06-13 05:31:46 -070023
24TaskService::~TaskService() {
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +000025 std::vector<std::unique_ptr<base::Thread>> threads;
26 {
27 base::AutoLock lock(lock_);
28 threads = std::move(threads_);
29 DCHECK_EQ(kInvalidInstanceId, bound_instance_id_);
30 }
31 // Should not have any lock to perform thread joins on thread destruction.
32 // All posted tasks should run before quitting the thread message loop.
33 threads.clear();
toyoshimccb8ff92017-06-13 05:31:46 -070034}
35
36bool TaskService::BindInstance() {
toyoshimccb8ff92017-06-13 05:31:46 -070037 base::AutoLock lock(lock_);
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +000038 if (bound_instance_id_ != kInvalidInstanceId)
39 return false;
40 bound_instance_id_ = next_instance_id_++;
41
toyoshimccb8ff92017-06-13 05:31:46 -070042 DCHECK(!default_task_runner_);
43 default_task_runner_ = base::ThreadTaskRunnerHandle::Get();
44 return true;
45}
46
47bool TaskService::UnbindInstance() {
48 {
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +000049 base::AutoLock lock(lock_);
toyoshimccb8ff92017-06-13 05:31:46 -070050 if (bound_instance_id_ == kInvalidInstanceId)
51 return false;
52 bound_instance_id_ = kInvalidInstanceId;
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +000053
54 DCHECK(default_task_runner_);
55 default_task_runner_ = nullptr;
toyoshimccb8ff92017-06-13 05:31:46 -070056 }
Robert Liaof66a9cf2017-08-26 01:40:35 +000057
toyoshimccb8ff92017-06-13 05:31:46 -070058 // From now on RunTask will never run any task bound to the instance id.
Robert Liaof66a9cf2017-08-26 01:40:35 +000059 // But invoked tasks might be still running here. To ensure no task runs on
60 // quitting this method, wait for all tasks to complete.
61 base::AutoLock tasks_in_flight_auto_lock(tasks_in_flight_lock_);
62 while (tasks_in_flight_ > 0)
63 no_tasks_in_flight_cv_.Wait();
64
toyoshimccb8ff92017-06-13 05:31:46 -070065 return true;
66}
67
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +000068bool TaskService::IsOnTaskRunner(RunnerId runner_id) {
69 base::AutoLock lock(lock_);
70 if (bound_instance_id_ == kInvalidInstanceId)
71 return false;
72
73 if (runner_id == kDefaultRunnerId)
74 return default_task_runner_->BelongsToCurrentThread();
75
76 size_t thread = runner_id - 1;
77 if (threads_.size() <= thread || !threads_[thread])
78 return false;
79
80 return threads_[thread]->task_runner()->BelongsToCurrentThread();
81}
82
toyoshimccb8ff92017-06-13 05:31:46 -070083void TaskService::PostStaticTask(RunnerId runner_id, base::OnceClosure task) {
toyoshimccb8ff92017-06-13 05:31:46 -070084 {
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +000085 // Disallow to post a task when no instance is bound, so that new threads
86 // can not be created after the thread finalization in the destructor.
toyoshimccb8ff92017-06-13 05:31:46 -070087 base::AutoLock lock(lock_);
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +000088 if (bound_instance_id_ == kInvalidInstanceId)
89 return;
toyoshimccb8ff92017-06-13 05:31:46 -070090 }
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +000091 scoped_refptr<base::SingleThreadTaskRunner> runner;
92 GetTaskRunner(runner_id)->PostTask(FROM_HERE, std::move(task));
toyoshimccb8ff92017-06-13 05:31:46 -070093}
94
95void TaskService::PostBoundTask(RunnerId runner_id, base::OnceClosure task) {
toyoshimccb8ff92017-06-13 05:31:46 -070096 InstanceId instance_id;
97 {
98 base::AutoLock lock(lock_);
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +000099 if (bound_instance_id_ == kInvalidInstanceId)
100 return;
toyoshimccb8ff92017-06-13 05:31:46 -0700101 instance_id = bound_instance_id_;
102 }
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000103 GetTaskRunner(runner_id)->PostTask(
104 FROM_HERE, base::BindOnce(&TaskService::RunTask, base::Unretained(this),
105 instance_id, runner_id, std::move(task)));
toyoshimccb8ff92017-06-13 05:31:46 -0700106}
107
108void TaskService::PostBoundDelayedTask(RunnerId runner_id,
109 base::OnceClosure task,
110 base::TimeDelta delay) {
toyoshimccb8ff92017-06-13 05:31:46 -0700111 InstanceId instance_id;
112 {
113 base::AutoLock lock(lock_);
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000114 if (bound_instance_id_ == kInvalidInstanceId)
115 return;
toyoshimccb8ff92017-06-13 05:31:46 -0700116 instance_id = bound_instance_id_;
117 }
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000118 GetTaskRunner(runner_id)->PostDelayedTask(
toyoshimccb8ff92017-06-13 05:31:46 -0700119 FROM_HERE,
120 base::BindOnce(&TaskService::RunTask, base::Unretained(this), instance_id,
121 runner_id, std::move(task)),
122 delay);
123}
124
125scoped_refptr<base::SingleThreadTaskRunner> TaskService::GetTaskRunner(
126 RunnerId runner_id) {
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000127 base::AutoLock lock(lock_);
toyoshimccb8ff92017-06-13 05:31:46 -0700128 if (runner_id == kDefaultRunnerId)
129 return default_task_runner_;
130
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000131 if (threads_.size() < runner_id)
132 threads_.resize(runner_id);
toyoshimccb8ff92017-06-13 05:31:46 -0700133
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000134 size_t thread = runner_id - 1;
135 if (!threads_[thread]) {
136 threads_[thread] = base::MakeUnique<base::Thread>(
toyoshimccb8ff92017-06-13 05:31:46 -0700137 base::StringPrintf("MidiService_TaskService_Thread(%zu)", runner_id));
138#if defined(OS_WIN)
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000139 threads_[thread]->init_com_with_mta(true);
toyoshimccb8ff92017-06-13 05:31:46 -0700140#endif
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000141 threads_[thread]->Start();
toyoshimccb8ff92017-06-13 05:31:46 -0700142 }
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000143 return threads_[thread]->task_runner();
toyoshimccb8ff92017-06-13 05:31:46 -0700144}
145
146void TaskService::RunTask(InstanceId instance_id,
147 RunnerId runner_id,
148 base::OnceClosure task) {
toyoshimccb8ff92017-06-13 05:31:46 -0700149 {
Robert Liaof66a9cf2017-08-26 01:40:35 +0000150 base::AutoLock tasks_in_flight_auto_lock(tasks_in_flight_lock_);
151 ++tasks_in_flight_;
toyoshimccb8ff92017-06-13 05:31:46 -0700152 }
Robert Liaof66a9cf2017-08-26 01:40:35 +0000153
154 if (IsInstanceIdStillBound(instance_id))
155 std::move(task).Run();
156
157 {
158 base::AutoLock tasks_in_flight_auto_lock(tasks_in_flight_lock_);
159 --tasks_in_flight_;
160 DCHECK_GE(tasks_in_flight_, 0);
161 if (tasks_in_flight_ == 0)
162 no_tasks_in_flight_cv_.Signal();
163 }
164}
165
166bool TaskService::IsInstanceIdStillBound(InstanceId instance_id) {
167 base::AutoLock lock(lock_);
168 return instance_id == bound_instance_id_;
toyoshimccb8ff92017-06-13 05:31:46 -0700169}
170
171} // namespace midi