blob: e54a01696c5b9446479d28bd6cc78c6595ce1bcf [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
Alexander Timin47c15312018-11-01 19:43:28 +00007#include "base/message_loop/message_loop_forward.h"
toyoshimccb8ff92017-06-13 05:31:46 -07008#include "base/strings/stringprintf.h"
Takashi Toyoshima2b92f3b2018-01-09 15:06:22 +09009#include "base/threading/thread_restrictions.h"
toyoshimccb8ff92017-06-13 05:31:46 -070010#include "base/threading/thread_task_runner_handle.h"
Robert Sesek4c4e5e22017-09-26 19:09:25 +000011#include "build/build_config.h"
toyoshimccb8ff92017-06-13 05:31:46 -070012
13namespace midi {
14
15namespace {
16
17constexpr TaskService::InstanceId kInvalidInstanceId = -1;
18
19} // namespace
20
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +000021constexpr TaskService::RunnerId TaskService::kDefaultRunnerId;
22
toyoshimccb8ff92017-06-13 05:31:46 -070023TaskService::TaskService()
Robert Liaof66a9cf2017-08-26 01:40:35 +000024 : no_tasks_in_flight_cv_(&tasks_in_flight_lock_),
25 tasks_in_flight_(0),
26 next_instance_id_(0),
Robert Liaoa4512b62017-08-29 16:38:28 +000027 bound_instance_id_(kInvalidInstanceId) {
28 DETACH_FROM_SEQUENCE(instance_binding_sequence_checker_);
29}
toyoshimccb8ff92017-06-13 05:31:46 -070030
31TaskService::~TaskService() {
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +000032 std::vector<std::unique_ptr<base::Thread>> threads;
33 {
34 base::AutoLock lock(lock_);
35 threads = std::move(threads_);
36 DCHECK_EQ(kInvalidInstanceId, bound_instance_id_);
37 }
38 // Should not have any lock to perform thread joins on thread destruction.
39 // All posted tasks should run before quitting the thread message loop.
40 threads.clear();
toyoshimccb8ff92017-06-13 05:31:46 -070041}
42
43bool TaskService::BindInstance() {
Robert Liaoa4512b62017-08-29 16:38:28 +000044 DCHECK_CALLED_ON_VALID_SEQUENCE(instance_binding_sequence_checker_);
toyoshimccb8ff92017-06-13 05:31:46 -070045 base::AutoLock lock(lock_);
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +000046 if (bound_instance_id_ != kInvalidInstanceId)
47 return false;
48 bound_instance_id_ = next_instance_id_++;
49
toyoshimccb8ff92017-06-13 05:31:46 -070050 DCHECK(!default_task_runner_);
51 default_task_runner_ = base::ThreadTaskRunnerHandle::Get();
52 return true;
53}
54
55bool TaskService::UnbindInstance() {
Robert Liaoa4512b62017-08-29 16:38:28 +000056 DCHECK_CALLED_ON_VALID_SEQUENCE(instance_binding_sequence_checker_);
toyoshimccb8ff92017-06-13 05:31:46 -070057 {
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +000058 base::AutoLock lock(lock_);
toyoshimccb8ff92017-06-13 05:31:46 -070059 if (bound_instance_id_ == kInvalidInstanceId)
60 return false;
61 bound_instance_id_ = kInvalidInstanceId;
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +000062
63 DCHECK(default_task_runner_);
64 default_task_runner_ = nullptr;
toyoshimccb8ff92017-06-13 05:31:46 -070065 }
Robert Liaof66a9cf2017-08-26 01:40:35 +000066
toyoshimccb8ff92017-06-13 05:31:46 -070067 // From now on RunTask will never run any task bound to the instance id.
Robert Liaof66a9cf2017-08-26 01:40:35 +000068 // But invoked tasks might be still running here. To ensure no task runs on
69 // quitting this method, wait for all tasks to complete.
70 base::AutoLock tasks_in_flight_auto_lock(tasks_in_flight_lock_);
Takashi Toyoshima2b92f3b2018-01-09 15:06:22 +090071 // TODO(https://crbug.com/796830): Remove sync operations on the I/O thread.
Raymes Khouryfa472312018-01-15 02:37:35 +000072 base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait;
Robert Liaof66a9cf2017-08-26 01:40:35 +000073 while (tasks_in_flight_ > 0)
74 no_tasks_in_flight_cv_.Wait();
75
toyoshimccb8ff92017-06-13 05:31:46 -070076 return true;
77}
78
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +000079bool TaskService::IsOnTaskRunner(RunnerId runner_id) {
80 base::AutoLock lock(lock_);
81 if (bound_instance_id_ == kInvalidInstanceId)
82 return false;
83
84 if (runner_id == kDefaultRunnerId)
85 return default_task_runner_->BelongsToCurrentThread();
86
87 size_t thread = runner_id - 1;
88 if (threads_.size() <= thread || !threads_[thread])
89 return false;
90
91 return threads_[thread]->task_runner()->BelongsToCurrentThread();
92}
93
toyoshimccb8ff92017-06-13 05:31:46 -070094void TaskService::PostStaticTask(RunnerId runner_id, base::OnceClosure task) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +000095 DCHECK_NE(kDefaultRunnerId, runner_id);
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +000096 GetTaskRunner(runner_id)->PostTask(FROM_HERE, std::move(task));
toyoshimccb8ff92017-06-13 05:31:46 -070097}
98
99void TaskService::PostBoundTask(RunnerId runner_id, base::OnceClosure task) {
toyoshimccb8ff92017-06-13 05:31:46 -0700100 InstanceId instance_id;
101 {
102 base::AutoLock lock(lock_);
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000103 if (bound_instance_id_ == kInvalidInstanceId)
104 return;
toyoshimccb8ff92017-06-13 05:31:46 -0700105 instance_id = bound_instance_id_;
106 }
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000107 GetTaskRunner(runner_id)->PostTask(
108 FROM_HERE, base::BindOnce(&TaskService::RunTask, base::Unretained(this),
109 instance_id, runner_id, std::move(task)));
toyoshimccb8ff92017-06-13 05:31:46 -0700110}
111
112void TaskService::PostBoundDelayedTask(RunnerId runner_id,
113 base::OnceClosure task,
114 base::TimeDelta delay) {
toyoshimccb8ff92017-06-13 05:31:46 -0700115 InstanceId instance_id;
116 {
117 base::AutoLock lock(lock_);
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000118 if (bound_instance_id_ == kInvalidInstanceId)
119 return;
toyoshimccb8ff92017-06-13 05:31:46 -0700120 instance_id = bound_instance_id_;
121 }
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000122 GetTaskRunner(runner_id)->PostDelayedTask(
toyoshimccb8ff92017-06-13 05:31:46 -0700123 FROM_HERE,
124 base::BindOnce(&TaskService::RunTask, base::Unretained(this), instance_id,
125 runner_id, std::move(task)),
126 delay);
127}
128
129scoped_refptr<base::SingleThreadTaskRunner> TaskService::GetTaskRunner(
130 RunnerId runner_id) {
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000131 base::AutoLock lock(lock_);
toyoshimccb8ff92017-06-13 05:31:46 -0700132 if (runner_id == kDefaultRunnerId)
133 return default_task_runner_;
134
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000135 if (threads_.size() < runner_id)
136 threads_.resize(runner_id);
toyoshimccb8ff92017-06-13 05:31:46 -0700137
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000138 size_t thread = runner_id - 1;
139 if (!threads_[thread]) {
Gyuyoung Kim34e191a2018-01-10 09:48:42 +0000140 threads_[thread] = std::make_unique<base::Thread>(
toyoshimccb8ff92017-06-13 05:31:46 -0700141 base::StringPrintf("MidiService_TaskService_Thread(%zu)", runner_id));
Robert Sesek4c4e5e22017-09-26 19:09:25 +0000142 base::Thread::Options options;
toyoshimccb8ff92017-06-13 05:31:46 -0700143#if defined(OS_WIN)
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000144 threads_[thread]->init_com_with_mta(true);
Robert Sesek4c4e5e22017-09-26 19:09:25 +0000145#elif defined(OS_MACOSX)
146 options.message_loop_type = base::MessageLoop::TYPE_UI;
toyoshimccb8ff92017-06-13 05:31:46 -0700147#endif
Robert Sesek4c4e5e22017-09-26 19:09:25 +0000148 threads_[thread]->StartWithOptions(options);
toyoshimccb8ff92017-06-13 05:31:46 -0700149 }
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000150 return threads_[thread]->task_runner();
toyoshimccb8ff92017-06-13 05:31:46 -0700151}
152
153void TaskService::RunTask(InstanceId instance_id,
154 RunnerId runner_id,
155 base::OnceClosure task) {
toyoshimccb8ff92017-06-13 05:31:46 -0700156 {
Robert Liaof66a9cf2017-08-26 01:40:35 +0000157 base::AutoLock tasks_in_flight_auto_lock(tasks_in_flight_lock_);
158 ++tasks_in_flight_;
toyoshimccb8ff92017-06-13 05:31:46 -0700159 }
Robert Liaof66a9cf2017-08-26 01:40:35 +0000160
161 if (IsInstanceIdStillBound(instance_id))
162 std::move(task).Run();
163
164 {
165 base::AutoLock tasks_in_flight_auto_lock(tasks_in_flight_lock_);
166 --tasks_in_flight_;
167 DCHECK_GE(tasks_in_flight_, 0);
168 if (tasks_in_flight_ == 0)
169 no_tasks_in_flight_cv_.Signal();
170 }
171}
172
173bool TaskService::IsInstanceIdStillBound(InstanceId instance_id) {
174 base::AutoLock lock(lock_);
175 return instance_id == bound_instance_id_;
toyoshimccb8ff92017-06-13 05:31:46 -0700176}
177
178} // namespace midi