blob: 7323c47edd2fdfc04d4a556fbf6d7397ec01be2d [file] [log] [blame]
Jordan Baylesb0c191e2019-03-26 15:49:57 -07001// Copyright 2019 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
Jordan Baylesa26582d2019-07-10 14:44:58 -07005#include "platform/impl/task_runner.h"
Jordan Baylesb0c191e2019-03-26 15:49:57 -07006
Ryan Keanea973b512019-07-29 15:50:39 -07007#include <thread>
8
Jordan Baylesb0c191e2019-03-26 15:49:57 -07009#include "platform/api/logging.h"
10
11namespace openscreen {
12namespace platform {
btolschc92ba2f2019-04-10 11:46:01 -070013
btolschd94fe622019-05-09 14:21:40 -070014TaskRunnerImpl::TaskRunnerImpl(platform::ClockNowFunctionPtr now_function,
15 TaskWaiter* event_waiter,
16 Clock::duration waiter_timeout)
17 : now_function_(now_function),
18 is_running_(false),
19 task_waiter_(event_waiter),
20 waiter_timeout_(waiter_timeout) {}
Jordan Baylesb0c191e2019-03-26 15:49:57 -070021
Jordan Baylesa8e96772019-04-08 10:53:54 -070022TaskRunnerImpl::~TaskRunnerImpl() = default;
Jordan Baylesb0c191e2019-03-26 15:49:57 -070023
Yuri Wiitalab929b832019-06-05 17:13:15 -070024void TaskRunnerImpl::PostPackagedTask(Task task) {
Jordan Baylesb0c191e2019-03-26 15:49:57 -070025 std::lock_guard<std::mutex> lock(task_mutex_);
Ryan Keaneefab2ed2019-07-22 12:36:53 -070026 tasks_.emplace_back(std::move(task));
btolschd94fe622019-05-09 14:21:40 -070027 if (task_waiter_) {
28 task_waiter_->OnTaskPosted();
29 } else {
30 run_loop_wakeup_.notify_one();
31 }
Jordan Baylesb0c191e2019-03-26 15:49:57 -070032}
33
Yuri Wiitalab929b832019-06-05 17:13:15 -070034void TaskRunnerImpl::PostPackagedTaskWithDelay(Task task,
35 Clock::duration delay) {
Jordan Baylesb0c191e2019-03-26 15:49:57 -070036 std::lock_guard<std::mutex> lock(task_mutex_);
Yuri Wiitalab929b832019-06-05 17:13:15 -070037 delayed_tasks_.emplace(
38 std::make_pair(now_function_() + delay, std::move(task)));
btolschd94fe622019-05-09 14:21:40 -070039 if (task_waiter_) {
40 task_waiter_->OnTaskPosted();
41 } else {
42 run_loop_wakeup_.notify_one();
43 }
Jordan Baylesb0c191e2019-03-26 15:49:57 -070044}
45
Max Yakimakhabf567dc2019-09-20 13:37:04 -070046bool TaskRunnerImpl::IsRunningOnTaskRunner() {
47 return task_runner_thread_id_ == std::this_thread::get_id();
48}
49
Jordan Baylesb0c191e2019-03-26 15:49:57 -070050void TaskRunnerImpl::RunUntilStopped() {
Yuri Wiitalac05ada22019-09-24 14:25:19 -070051 OSP_CHECK(!is_running_);
Max Yakimakhabf567dc2019-09-20 13:37:04 -070052 task_runner_thread_id_ = std::this_thread::get_id();
Yuri Wiitalac05ada22019-09-24 14:25:19 -070053 is_running_ = true;
Jordan Baylesb0c191e2019-03-26 15:49:57 -070054
Yuri Wiitalac05ada22019-09-24 14:25:19 -070055 // Main loop: Run until the |is_running_| flag is set back to false by the
56 // "quit task" posted by RequestStopSoon().
57 while (is_running_) {
58 ScheduleDelayedTasks();
Yuri Wiitala71a84bc2019-09-25 12:00:23 -070059 if (GrabMoreRunnableTasks()) {
60 RunRunnableTasks();
61 }
Yuri Wiitalac05ada22019-09-24 14:25:19 -070062 }
63
64 // Flushing phase: Ensure all immediately-runnable tasks are run before
65 // returning. Since running some tasks might cause more immediately-runnable
66 // tasks to be posted, loop until there is no more work.
Yuri Wiitala71a84bc2019-09-25 12:00:23 -070067 //
68 // If there is bad code that posts tasks indefinitely, this loop will never
69 // break. However, that also means there is a code path spinning a CPU core at
70 // 100% all the time. Rather than mitigate this problem scenario, purposely
71 // let it manifest here in the hopes that unit testing will reveal it (e.g., a
72 // unit test that never finishes running).
73 while (GrabMoreRunnableTasks()) {
74 RunRunnableTasks();
Yuri Wiitalac05ada22019-09-24 14:25:19 -070075 }
76
77 task_runner_thread_id_ = std::thread::id();
Jordan Baylesb0c191e2019-03-26 15:49:57 -070078}
79
80void TaskRunnerImpl::RequestStopSoon() {
Yuri Wiitalac05ada22019-09-24 14:25:19 -070081 PostTask([this]() { is_running_ = false; });
Jordan Baylesb0c191e2019-03-26 15:49:57 -070082}
83
Yuri Wiitalac05ada22019-09-24 14:25:19 -070084void TaskRunnerImpl::RunRunnableTasks() {
Yuri Wiitalab929b832019-06-05 17:13:15 -070085 OSP_DVLOG << "Running " << running_tasks_.size() << " tasks...";
Max Yakimakha371bc2b2019-09-04 10:49:17 -070086 for (TaskWithMetadata& running_task : running_tasks_) {
Yuri Wiitalab929b832019-06-05 17:13:15 -070087 // Move the task to the stack so that its bound state is freed immediately
88 // after being run.
Max Yakimakha371bc2b2019-09-04 10:49:17 -070089 TaskWithMetadata task = std::move(running_task);
90 task();
Jordan Baylesb0c191e2019-03-26 15:49:57 -070091 }
Yuri Wiitalab929b832019-06-05 17:13:15 -070092 running_tasks_.clear();
Jordan Baylesb0c191e2019-03-26 15:49:57 -070093}
94
Jordan Baylesb0c191e2019-03-26 15:49:57 -070095void TaskRunnerImpl::ScheduleDelayedTasks() {
96 std::lock_guard<std::mutex> lock(task_mutex_);
97
98 // Getting the time can be expensive on some platforms, so only get it once.
99 const auto current_time = now_function_();
Yuri Wiitalab929b832019-06-05 17:13:15 -0700100 const auto end_of_range = delayed_tasks_.upper_bound(current_time);
101 for (auto it = delayed_tasks_.begin(); it != end_of_range; ++it) {
102 tasks_.push_back(std::move(it->second));
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700103 }
Yuri Wiitalab929b832019-06-05 17:13:15 -0700104 delayed_tasks_.erase(delayed_tasks_.begin(), end_of_range);
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700105}
106
Jordan Bayles5d72bc22019-04-09 13:33:52 -0700107bool TaskRunnerImpl::ShouldWakeUpRunLoop() {
108 if (!is_running_) {
109 return true;
110 }
111
112 if (!tasks_.empty()) {
113 return true;
114 }
115
116 return !delayed_tasks_.empty() &&
Yuri Wiitalab929b832019-06-05 17:13:15 -0700117 (delayed_tasks_.begin()->first <= now_function_());
Jordan Bayles5d72bc22019-04-09 13:33:52 -0700118}
119
Yuri Wiitala71a84bc2019-09-25 12:00:23 -0700120bool TaskRunnerImpl::GrabMoreRunnableTasks() {
121 OSP_DCHECK(running_tasks_.empty());
122
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700123 std::unique_lock<std::mutex> lock(task_mutex_);
Jordan Baylesa8e96772019-04-08 10:53:54 -0700124 if (!tasks_.empty()) {
Yuri Wiitala71a84bc2019-09-25 12:00:23 -0700125 running_tasks_.swap(tasks_);
126 return true;
127 }
128
129 if (!is_running_) {
130 return false; // Stop was requested. Don't wait for more tasks.
Jordan Baylesa8e96772019-04-08 10:53:54 -0700131 }
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700132
btolschd94fe622019-05-09 14:21:40 -0700133 if (task_waiter_) {
134 do {
135 Clock::duration timeout = waiter_timeout_;
136 if (!delayed_tasks_.empty()) {
137 Clock::duration next_task_delta =
Yuri Wiitalab929b832019-06-05 17:13:15 -0700138 delayed_tasks_.begin()->first - now_function_();
btolschd94fe622019-05-09 14:21:40 -0700139 if (next_task_delta < timeout) {
140 timeout = next_task_delta;
141 }
142 }
143 lock.unlock();
144 task_waiter_->WaitForTaskToBePosted(timeout);
145 lock.lock();
146 } while (!ShouldWakeUpRunLoop());
Jordan Bayles5d72bc22019-04-09 13:33:52 -0700147 } else {
btolschd94fe622019-05-09 14:21:40 -0700148 // Pass a wait predicate to avoid lost or spurious wakeups.
Yuri Wiitalac05ada22019-09-24 14:25:19 -0700149 const auto wait_predicate = [this] { return ShouldWakeUpRunLoop(); };
btolschd94fe622019-05-09 14:21:40 -0700150 if (!delayed_tasks_.empty()) {
151 // We don't have any work to do currently, but have some in the
152 // pipe.
153 OSP_DVLOG << "TaskRunner waiting for lock until delayed task ready...";
Yuri Wiitalab929b832019-06-05 17:13:15 -0700154 run_loop_wakeup_.wait_for(lock,
155 delayed_tasks_.begin()->first - now_function_(),
156 wait_predicate);
btolschd94fe622019-05-09 14:21:40 -0700157 } else {
158 // We don't have any work queued.
btolsch4051e722019-06-07 16:15:17 -0700159 OSP_DVLOG << "TaskRunnerImpl waiting for lock...";
btolschd94fe622019-05-09 14:21:40 -0700160 run_loop_wakeup_.wait(lock, wait_predicate);
161 }
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700162 }
163
Yuri Wiitala71a84bc2019-09-25 12:00:23 -0700164 return false;
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700165}
Yuri Wiitala71a84bc2019-09-25 12:00:23 -0700166
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700167} // namespace platform
168} // namespace openscreen