blob: 667201f86e8bb85e91ce7d35c8f7c48dbfadd503 [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
Gabriel Charette9dc55072017-11-21 20:59:31 +00007#include "base/message_loop/message_loop.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
21TaskService::TaskService()
Robert Liaof66a9cf2017-08-26 01:40:35 +000022 : no_tasks_in_flight_cv_(&tasks_in_flight_lock_),
23 tasks_in_flight_(0),
24 next_instance_id_(0),
Robert Liaoa4512b62017-08-29 16:38:28 +000025 bound_instance_id_(kInvalidInstanceId) {
26 DETACH_FROM_SEQUENCE(instance_binding_sequence_checker_);
27}
toyoshimccb8ff92017-06-13 05:31:46 -070028
29TaskService::~TaskService() {
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +000030 std::vector<std::unique_ptr<base::Thread>> threads;
31 {
32 base::AutoLock lock(lock_);
33 threads = std::move(threads_);
34 DCHECK_EQ(kInvalidInstanceId, bound_instance_id_);
35 }
36 // Should not have any lock to perform thread joins on thread destruction.
37 // All posted tasks should run before quitting the thread message loop.
38 threads.clear();
toyoshimccb8ff92017-06-13 05:31:46 -070039}
40
41bool TaskService::BindInstance() {
Robert Liaoa4512b62017-08-29 16:38:28 +000042 DCHECK_CALLED_ON_VALID_SEQUENCE(instance_binding_sequence_checker_);
toyoshimccb8ff92017-06-13 05:31:46 -070043 base::AutoLock lock(lock_);
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +000044 if (bound_instance_id_ != kInvalidInstanceId)
45 return false;
46 bound_instance_id_ = next_instance_id_++;
47
toyoshimccb8ff92017-06-13 05:31:46 -070048 DCHECK(!default_task_runner_);
49 default_task_runner_ = base::ThreadTaskRunnerHandle::Get();
50 return true;
51}
52
53bool TaskService::UnbindInstance() {
Robert Liaoa4512b62017-08-29 16:38:28 +000054 DCHECK_CALLED_ON_VALID_SEQUENCE(instance_binding_sequence_checker_);
toyoshimccb8ff92017-06-13 05:31:46 -070055 {
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +000056 base::AutoLock lock(lock_);
toyoshimccb8ff92017-06-13 05:31:46 -070057 if (bound_instance_id_ == kInvalidInstanceId)
58 return false;
59 bound_instance_id_ = kInvalidInstanceId;
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +000060
61 DCHECK(default_task_runner_);
62 default_task_runner_ = nullptr;
toyoshimccb8ff92017-06-13 05:31:46 -070063 }
Robert Liaof66a9cf2017-08-26 01:40:35 +000064
toyoshimccb8ff92017-06-13 05:31:46 -070065 // From now on RunTask will never run any task bound to the instance id.
Robert Liaof66a9cf2017-08-26 01:40:35 +000066 // But invoked tasks might be still running here. To ensure no task runs on
67 // quitting this method, wait for all tasks to complete.
68 base::AutoLock tasks_in_flight_auto_lock(tasks_in_flight_lock_);
Takashi Toyoshima2b92f3b2018-01-09 15:06:22 +090069 // TODO(https://crbug.com/796830): Remove sync operations on the I/O thread.
Raymes Khouryfa472312018-01-15 02:37:35 +000070 base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait;
Robert Liaof66a9cf2017-08-26 01:40:35 +000071 while (tasks_in_flight_ > 0)
72 no_tasks_in_flight_cv_.Wait();
73
toyoshimccb8ff92017-06-13 05:31:46 -070074 return true;
75}
76
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +000077bool TaskService::IsOnTaskRunner(RunnerId runner_id) {
78 base::AutoLock lock(lock_);
79 if (bound_instance_id_ == kInvalidInstanceId)
80 return false;
81
82 if (runner_id == kDefaultRunnerId)
83 return default_task_runner_->BelongsToCurrentThread();
84
85 size_t thread = runner_id - 1;
86 if (threads_.size() <= thread || !threads_[thread])
87 return false;
88
89 return threads_[thread]->task_runner()->BelongsToCurrentThread();
90}
91
toyoshimccb8ff92017-06-13 05:31:46 -070092void TaskService::PostStaticTask(RunnerId runner_id, base::OnceClosure task) {
toyoshimccb8ff92017-06-13 05:31:46 -070093 {
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +000094 // Disallow to post a task when no instance is bound, so that new threads
95 // can not be created after the thread finalization in the destructor.
toyoshimccb8ff92017-06-13 05:31:46 -070096 base::AutoLock lock(lock_);
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +000097 if (bound_instance_id_ == kInvalidInstanceId)
98 return;
toyoshimccb8ff92017-06-13 05:31:46 -070099 }
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000100 scoped_refptr<base::SingleThreadTaskRunner> runner;
101 GetTaskRunner(runner_id)->PostTask(FROM_HERE, std::move(task));
toyoshimccb8ff92017-06-13 05:31:46 -0700102}
103
104void TaskService::PostBoundTask(RunnerId runner_id, base::OnceClosure task) {
toyoshimccb8ff92017-06-13 05:31:46 -0700105 InstanceId instance_id;
106 {
107 base::AutoLock lock(lock_);
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000108 if (bound_instance_id_ == kInvalidInstanceId)
109 return;
toyoshimccb8ff92017-06-13 05:31:46 -0700110 instance_id = bound_instance_id_;
111 }
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000112 GetTaskRunner(runner_id)->PostTask(
113 FROM_HERE, base::BindOnce(&TaskService::RunTask, base::Unretained(this),
114 instance_id, runner_id, std::move(task)));
toyoshimccb8ff92017-06-13 05:31:46 -0700115}
116
117void TaskService::PostBoundDelayedTask(RunnerId runner_id,
118 base::OnceClosure task,
119 base::TimeDelta delay) {
toyoshimccb8ff92017-06-13 05:31:46 -0700120 InstanceId instance_id;
121 {
122 base::AutoLock lock(lock_);
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000123 if (bound_instance_id_ == kInvalidInstanceId)
124 return;
toyoshimccb8ff92017-06-13 05:31:46 -0700125 instance_id = bound_instance_id_;
126 }
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000127 GetTaskRunner(runner_id)->PostDelayedTask(
toyoshimccb8ff92017-06-13 05:31:46 -0700128 FROM_HERE,
129 base::BindOnce(&TaskService::RunTask, base::Unretained(this), instance_id,
130 runner_id, std::move(task)),
131 delay);
132}
133
134scoped_refptr<base::SingleThreadTaskRunner> TaskService::GetTaskRunner(
135 RunnerId runner_id) {
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000136 base::AutoLock lock(lock_);
toyoshimccb8ff92017-06-13 05:31:46 -0700137 if (runner_id == kDefaultRunnerId)
138 return default_task_runner_;
139
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000140 if (threads_.size() < runner_id)
141 threads_.resize(runner_id);
toyoshimccb8ff92017-06-13 05:31:46 -0700142
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000143 size_t thread = runner_id - 1;
144 if (!threads_[thread]) {
Gyuyoung Kim34e191a2018-01-10 09:48:42 +0000145 threads_[thread] = std::make_unique<base::Thread>(
toyoshimccb8ff92017-06-13 05:31:46 -0700146 base::StringPrintf("MidiService_TaskService_Thread(%zu)", runner_id));
Robert Sesek4c4e5e22017-09-26 19:09:25 +0000147 base::Thread::Options options;
toyoshimccb8ff92017-06-13 05:31:46 -0700148#if defined(OS_WIN)
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000149 threads_[thread]->init_com_with_mta(true);
Robert Sesek4c4e5e22017-09-26 19:09:25 +0000150#elif defined(OS_MACOSX)
151 options.message_loop_type = base::MessageLoop::TYPE_UI;
toyoshimccb8ff92017-06-13 05:31:46 -0700152#endif
Robert Sesek4c4e5e22017-09-26 19:09:25 +0000153 threads_[thread]->StartWithOptions(options);
toyoshimccb8ff92017-06-13 05:31:46 -0700154 }
Takashi Toyoshima5bbd0f82017-07-04 17:05:09 +0000155 return threads_[thread]->task_runner();
toyoshimccb8ff92017-06-13 05:31:46 -0700156}
157
158void TaskService::RunTask(InstanceId instance_id,
159 RunnerId runner_id,
160 base::OnceClosure task) {
toyoshimccb8ff92017-06-13 05:31:46 -0700161 {
Robert Liaof66a9cf2017-08-26 01:40:35 +0000162 base::AutoLock tasks_in_flight_auto_lock(tasks_in_flight_lock_);
163 ++tasks_in_flight_;
toyoshimccb8ff92017-06-13 05:31:46 -0700164 }
Robert Liaof66a9cf2017-08-26 01:40:35 +0000165
166 if (IsInstanceIdStillBound(instance_id))
167 std::move(task).Run();
168
169 {
170 base::AutoLock tasks_in_flight_auto_lock(tasks_in_flight_lock_);
171 --tasks_in_flight_;
172 DCHECK_GE(tasks_in_flight_, 0);
173 if (tasks_in_flight_ == 0)
174 no_tasks_in_flight_cv_.Signal();
175 }
176}
177
178bool TaskService::IsInstanceIdStillBound(InstanceId instance_id) {
179 base::AutoLock lock(lock_);
180 return instance_id == bound_instance_id_;
toyoshimccb8ff92017-06-13 05:31:46 -0700181}
182
183} // namespace midi