blob: ed245d28d3248ead3e606e7d8f8b41b266674111 [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
Sebastien Marchand2912d9f2019-01-25 16:49:37 +00007#include "base/bind.h"
Alexander Timind8fbc3f2018-11-01 20:15:20 +00008#include "base/message_loop/message_loop.h"
toyoshimccb8ff92017-06-13 05:31:46 -07009#include "base/strings/stringprintf.h"
Takashi Toyoshima2b92f3b2018-01-09 15:06:22 +090010#include "base/threading/thread_restrictions.h"
toyoshimccb8ff92017-06-13 05:31:46 -070011#include "base/threading/thread_task_runner_handle.h"
Robert Sesek4c4e5e22017-09-26 19:09:25 +000012#include "build/build_config.h"
toyoshimccb8ff92017-06-13 05:31:46 -070013
14namespace midi {
15
16namespace {
17
18constexpr TaskService::InstanceId kInvalidInstanceId = -1;
19
20} // namespace
21
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +000022constexpr TaskService::RunnerId TaskService::kDefaultRunnerId;
23
toyoshimccb8ff92017-06-13 05:31:46 -070024TaskService::TaskService()
Robert Liaof66a9cf2017-08-26 01:40:35 +000025 : no_tasks_in_flight_cv_(&tasks_in_flight_lock_),
26 tasks_in_flight_(0),
27 next_instance_id_(0),
Robert Liaoa4512b62017-08-29 16:38:28 +000028 bound_instance_id_(kInvalidInstanceId) {
29 DETACH_FROM_SEQUENCE(instance_binding_sequence_checker_);
30}
toyoshimccb8ff92017-06-13 05:31:46 -070031
32TaskService::~TaskService() {
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +000033 std::vector<std::unique_ptr<base::Thread>> threads;
34 {
35 base::AutoLock lock(lock_);
36 threads = std::move(threads_);
37 DCHECK_EQ(kInvalidInstanceId, bound_instance_id_);
38 }
39 // Should not have any lock to perform thread joins on thread destruction.
40 // All posted tasks should run before quitting the thread message loop.
41 threads.clear();
toyoshimccb8ff92017-06-13 05:31:46 -070042}
43
44bool TaskService::BindInstance() {
Robert Liaoa4512b62017-08-29 16:38:28 +000045 DCHECK_CALLED_ON_VALID_SEQUENCE(instance_binding_sequence_checker_);
toyoshimccb8ff92017-06-13 05:31:46 -070046 base::AutoLock lock(lock_);
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +000047 if (bound_instance_id_ != kInvalidInstanceId)
48 return false;
49 bound_instance_id_ = next_instance_id_++;
50
toyoshimccb8ff92017-06-13 05:31:46 -070051 DCHECK(!default_task_runner_);
52 default_task_runner_ = base::ThreadTaskRunnerHandle::Get();
53 return true;
54}
55
56bool TaskService::UnbindInstance() {
Robert Liaoa4512b62017-08-29 16:38:28 +000057 DCHECK_CALLED_ON_VALID_SEQUENCE(instance_binding_sequence_checker_);
toyoshimccb8ff92017-06-13 05:31:46 -070058 {
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +000059 base::AutoLock lock(lock_);
toyoshimccb8ff92017-06-13 05:31:46 -070060 if (bound_instance_id_ == kInvalidInstanceId)
61 return false;
62 bound_instance_id_ = kInvalidInstanceId;
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +000063
64 DCHECK(default_task_runner_);
65 default_task_runner_ = nullptr;
toyoshimccb8ff92017-06-13 05:31:46 -070066 }
Robert Liaof66a9cf2017-08-26 01:40:35 +000067
toyoshimccb8ff92017-06-13 05:31:46 -070068 // From now on RunTask will never run any task bound to the instance id.
Robert Liaof66a9cf2017-08-26 01:40:35 +000069 // But invoked tasks might be still running here. To ensure no task runs on
70 // quitting this method, wait for all tasks to complete.
71 base::AutoLock tasks_in_flight_auto_lock(tasks_in_flight_lock_);
Takashi Toyoshima2b92f3b2018-01-09 15:06:22 +090072 // TODO(https://crbug.com/796830): Remove sync operations on the I/O thread.
Raymes Khouryfa472312018-01-15 02:37:35 +000073 base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait;
Robert Liaof66a9cf2017-08-26 01:40:35 +000074 while (tasks_in_flight_ > 0)
75 no_tasks_in_flight_cv_.Wait();
76
toyoshimccb8ff92017-06-13 05:31:46 -070077 return true;
78}
79
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +000080bool TaskService::IsOnTaskRunner(RunnerId runner_id) {
81 base::AutoLock lock(lock_);
82 if (bound_instance_id_ == kInvalidInstanceId)
83 return false;
84
85 if (runner_id == kDefaultRunnerId)
86 return default_task_runner_->BelongsToCurrentThread();
87
88 size_t thread = runner_id - 1;
89 if (threads_.size() <= thread || !threads_[thread])
90 return false;
91
92 return threads_[thread]->task_runner()->BelongsToCurrentThread();
93}
94
toyoshimccb8ff92017-06-13 05:31:46 -070095void TaskService::PostStaticTask(RunnerId runner_id, base::OnceClosure task) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +000096 DCHECK_NE(kDefaultRunnerId, runner_id);
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +000097 GetTaskRunner(runner_id)->PostTask(FROM_HERE, std::move(task));
toyoshimccb8ff92017-06-13 05:31:46 -070098}
99
100void TaskService::PostBoundTask(RunnerId runner_id, base::OnceClosure task) {
toyoshimccb8ff92017-06-13 05:31:46 -0700101 InstanceId instance_id;
102 {
103 base::AutoLock lock(lock_);
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000104 if (bound_instance_id_ == kInvalidInstanceId)
105 return;
toyoshimccb8ff92017-06-13 05:31:46 -0700106 instance_id = bound_instance_id_;
107 }
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000108 GetTaskRunner(runner_id)->PostTask(
109 FROM_HERE, base::BindOnce(&TaskService::RunTask, base::Unretained(this),
110 instance_id, runner_id, std::move(task)));
toyoshimccb8ff92017-06-13 05:31:46 -0700111}
112
113void TaskService::PostBoundDelayedTask(RunnerId runner_id,
114 base::OnceClosure task,
115 base::TimeDelta delay) {
toyoshimccb8ff92017-06-13 05:31:46 -0700116 InstanceId instance_id;
117 {
118 base::AutoLock lock(lock_);
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000119 if (bound_instance_id_ == kInvalidInstanceId)
120 return;
toyoshimccb8ff92017-06-13 05:31:46 -0700121 instance_id = bound_instance_id_;
122 }
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000123 GetTaskRunner(runner_id)->PostDelayedTask(
toyoshimccb8ff92017-06-13 05:31:46 -0700124 FROM_HERE,
125 base::BindOnce(&TaskService::RunTask, base::Unretained(this), instance_id,
126 runner_id, std::move(task)),
127 delay);
128}
129
130scoped_refptr<base::SingleThreadTaskRunner> TaskService::GetTaskRunner(
131 RunnerId runner_id) {
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000132 base::AutoLock lock(lock_);
toyoshimccb8ff92017-06-13 05:31:46 -0700133 if (runner_id == kDefaultRunnerId)
134 return default_task_runner_;
135
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000136 if (threads_.size() < runner_id)
137 threads_.resize(runner_id);
toyoshimccb8ff92017-06-13 05:31:46 -0700138
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000139 size_t thread = runner_id - 1;
140 if (!threads_[thread]) {
Gyuyoung Kim34e191a2018-01-10 09:48:42 +0000141 threads_[thread] = std::make_unique<base::Thread>(
toyoshimccb8ff92017-06-13 05:31:46 -0700142 base::StringPrintf("MidiService_TaskService_Thread(%zu)", runner_id));
Robert Sesek4c4e5e22017-09-26 19:09:25 +0000143 base::Thread::Options options;
toyoshimccb8ff92017-06-13 05:31:46 -0700144#if defined(OS_WIN)
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000145 threads_[thread]->init_com_with_mta(true);
Robert Sesek4c4e5e22017-09-26 19:09:25 +0000146#elif defined(OS_MACOSX)
147 options.message_loop_type = base::MessageLoop::TYPE_UI;
toyoshimccb8ff92017-06-13 05:31:46 -0700148#endif
Robert Sesek4c4e5e22017-09-26 19:09:25 +0000149 threads_[thread]->StartWithOptions(options);
toyoshimccb8ff92017-06-13 05:31:46 -0700150 }
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000151 return threads_[thread]->task_runner();
toyoshimccb8ff92017-06-13 05:31:46 -0700152}
153
154void TaskService::RunTask(InstanceId instance_id,
155 RunnerId runner_id,
156 base::OnceClosure task) {
toyoshimccb8ff92017-06-13 05:31:46 -0700157 {
Robert Liaof66a9cf2017-08-26 01:40:35 +0000158 base::AutoLock tasks_in_flight_auto_lock(tasks_in_flight_lock_);
159 ++tasks_in_flight_;
toyoshimccb8ff92017-06-13 05:31:46 -0700160 }
Robert Liaof66a9cf2017-08-26 01:40:35 +0000161
162 if (IsInstanceIdStillBound(instance_id))
163 std::move(task).Run();
164
165 {
166 base::AutoLock tasks_in_flight_auto_lock(tasks_in_flight_lock_);
167 --tasks_in_flight_;
168 DCHECK_GE(tasks_in_flight_, 0);
169 if (tasks_in_flight_ == 0)
170 no_tasks_in_flight_cv_.Signal();
171 }
172}
173
174bool TaskService::IsInstanceIdStillBound(InstanceId instance_id) {
175 base::AutoLock lock(lock_);
176 return instance_id == bound_instance_id_;
toyoshimccb8ff92017-06-13 05:31:46 -0700177}
178
179} // namespace midi