task_service.cc 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. // 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. #include "media/midi/task_service.h"
  5. #include <limits>
  6. #include "base/bind.h"
  7. #include "base/message_loop/message_pump_type.h"
  8. #include "base/strings/stringprintf.h"
  9. #include "base/threading/thread_restrictions.h"
  10. #include "base/threading/thread_task_runner_handle.h"
  11. #include "build/build_config.h"
  12. namespace midi {
  13. constexpr TaskService::RunnerId TaskService::kDefaultRunnerId;
  14. constexpr TaskService::InstanceId TaskService::kInvalidInstanceId;
  15. TaskService::TaskService() : no_tasks_in_flight_cv_(&tasks_in_flight_lock_) {
  16. DETACH_FROM_SEQUENCE(instance_binding_sequence_checker_);
  17. }
  18. TaskService::~TaskService() {
  19. std::vector<std::unique_ptr<base::Thread>> threads;
  20. {
  21. base::AutoLock lock(lock_);
  22. threads = std::move(threads_);
  23. DCHECK_EQ(kInvalidInstanceId, bound_instance_id_);
  24. }
  25. // Should not have any lock to perform thread joins on thread destruction.
  26. // All posted tasks should run before quitting the thread message loop.
  27. threads.clear();
  28. }
  29. bool TaskService::BindInstance() {
  30. DCHECK_CALLED_ON_VALID_SEQUENCE(instance_binding_sequence_checker_);
  31. base::AutoLock lock(lock_);
  32. if (bound_instance_id_ != kInvalidInstanceId)
  33. return false;
  34. // If the InstanceId reaches to the limit, just fail rather than doing
  35. // something nicer for such impractical case.
  36. if (std::numeric_limits<InstanceId>::max() == next_instance_id_)
  37. return false;
  38. bound_instance_id_ = ++next_instance_id_;
  39. DCHECK(!default_task_runner_);
  40. default_task_runner_ = base::ThreadTaskRunnerHandle::Get();
  41. return true;
  42. }
  43. bool TaskService::UnbindInstance() {
  44. DCHECK_CALLED_ON_VALID_SEQUENCE(instance_binding_sequence_checker_);
  45. {
  46. base::AutoLock lock(lock_);
  47. if (bound_instance_id_ == kInvalidInstanceId)
  48. return false;
  49. DCHECK_EQ(next_instance_id_, bound_instance_id_);
  50. bound_instance_id_ = kInvalidInstanceId;
  51. DCHECK(default_task_runner_);
  52. default_task_runner_ = nullptr;
  53. }
  54. // From now on RunTask will never run any task bound to the instance id.
  55. // But invoked tasks might be still running here. To ensure no task runs on
  56. // quitting this method, wait for all tasks to complete.
  57. base::AutoLock tasks_in_flight_lock(tasks_in_flight_lock_);
  58. // TODO(https://crbug.com/796830): Remove sync operations on the I/O thread.
  59. base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait;
  60. while (tasks_in_flight_ > 0)
  61. no_tasks_in_flight_cv_.Wait();
  62. return true;
  63. }
  64. bool TaskService::IsOnTaskRunner(RunnerId runner_id) {
  65. base::AutoLock lock(lock_);
  66. if (bound_instance_id_ == kInvalidInstanceId)
  67. return false;
  68. if (runner_id == kDefaultRunnerId)
  69. return default_task_runner_->BelongsToCurrentThread();
  70. size_t thread = runner_id - 1;
  71. if (threads_.size() <= thread || !threads_[thread])
  72. return false;
  73. return threads_[thread]->task_runner()->BelongsToCurrentThread();
  74. }
  75. void TaskService::PostStaticTask(RunnerId runner_id, base::OnceClosure task) {
  76. DCHECK_NE(kDefaultRunnerId, runner_id);
  77. GetTaskRunner(runner_id)->PostTask(FROM_HERE, std::move(task));
  78. }
  79. void TaskService::PostBoundTask(RunnerId runner_id, base::OnceClosure task) {
  80. InstanceId instance_id;
  81. {
  82. base::AutoLock lock(lock_);
  83. if (bound_instance_id_ == kInvalidInstanceId)
  84. return;
  85. instance_id = bound_instance_id_;
  86. }
  87. GetTaskRunner(runner_id)->PostTask(
  88. FROM_HERE, base::BindOnce(&TaskService::RunTask, base::Unretained(this),
  89. instance_id, runner_id, std::move(task)));
  90. }
  91. void TaskService::PostBoundDelayedTask(RunnerId runner_id,
  92. base::OnceClosure task,
  93. base::TimeDelta delay) {
  94. InstanceId instance_id;
  95. {
  96. base::AutoLock lock(lock_);
  97. if (bound_instance_id_ == kInvalidInstanceId)
  98. return;
  99. instance_id = bound_instance_id_;
  100. }
  101. GetTaskRunner(runner_id)->PostDelayedTask(
  102. FROM_HERE,
  103. base::BindOnce(&TaskService::RunTask, base::Unretained(this), instance_id,
  104. runner_id, std::move(task)),
  105. delay);
  106. }
  107. void TaskService::OverflowInstanceIdForTesting() {
  108. next_instance_id_ = std::numeric_limits<InstanceId>::max();
  109. }
  110. scoped_refptr<base::SingleThreadTaskRunner> TaskService::GetTaskRunner(
  111. RunnerId runner_id) {
  112. base::AutoLock lock(lock_);
  113. if (runner_id == kDefaultRunnerId)
  114. return default_task_runner_;
  115. if (threads_.size() < runner_id)
  116. threads_.resize(runner_id);
  117. size_t thread = runner_id - 1;
  118. if (!threads_[thread]) {
  119. threads_[thread] = std::make_unique<base::Thread>(
  120. base::StringPrintf("MidiService_TaskService_Thread(%zu)", runner_id));
  121. base::Thread::Options options;
  122. #if BUILDFLAG(IS_WIN)
  123. threads_[thread]->init_com_with_mta(true);
  124. #elif BUILDFLAG(IS_MAC)
  125. options.message_pump_type = base::MessagePumpType::UI;
  126. #endif
  127. threads_[thread]->StartWithOptions(std::move(options));
  128. }
  129. return threads_[thread]->task_runner();
  130. }
  131. void TaskService::RunTask(InstanceId instance_id,
  132. RunnerId runner_id,
  133. base::OnceClosure task) {
  134. {
  135. base::AutoLock tasks_in_flight_lock(tasks_in_flight_lock_);
  136. ++tasks_in_flight_;
  137. }
  138. if (IsInstanceIdStillBound(instance_id))
  139. std::move(task).Run();
  140. {
  141. base::AutoLock tasks_in_flight_lock(tasks_in_flight_lock_);
  142. --tasks_in_flight_;
  143. DCHECK_GE(tasks_in_flight_, 0);
  144. if (tasks_in_flight_ == 0)
  145. no_tasks_in_flight_cv_.Signal();
  146. }
  147. }
  148. bool TaskService::IsInstanceIdStillBound(InstanceId instance_id) {
  149. base::AutoLock lock(lock_);
  150. return instance_id == bound_instance_id_;
  151. }
  152. } // namespace midi