1 // Copyright 2017 the V8 project 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 "src/libplatform/default-foreground-task-runner.h"
6
7 #include "src/base/platform/mutex.h"
8 #include "src/libplatform/default-platform.h"
9
10 namespace v8 {
11 namespace platform {
12
RunTaskScope(std::shared_ptr<DefaultForegroundTaskRunner> task_runner)13 DefaultForegroundTaskRunner::RunTaskScope::RunTaskScope(
14 std::shared_ptr<DefaultForegroundTaskRunner> task_runner)
15 : task_runner_(task_runner) {
16 DCHECK_GE(task_runner->nesting_depth_, 0);
17 task_runner->nesting_depth_++;
18 }
19
~RunTaskScope()20 DefaultForegroundTaskRunner::RunTaskScope::~RunTaskScope() {
21 DCHECK_GT(task_runner_->nesting_depth_, 0);
22 task_runner_->nesting_depth_--;
23 }
24
DefaultForegroundTaskRunner(IdleTaskSupport idle_task_support,TimeFunction time_function)25 DefaultForegroundTaskRunner::DefaultForegroundTaskRunner(
26 IdleTaskSupport idle_task_support, TimeFunction time_function)
27 : idle_task_support_(idle_task_support), time_function_(time_function) {}
28
Terminate()29 void DefaultForegroundTaskRunner::Terminate() {
30 base::MutexGuard guard(&lock_);
31 terminated_ = true;
32
33 // Drain the task queues.
34 while (!task_queue_.empty()) task_queue_.pop_front();
35 while (!delayed_task_queue_.empty()) delayed_task_queue_.pop();
36 while (!idle_task_queue_.empty()) idle_task_queue_.pop();
37 }
38
PostTaskLocked(std::unique_ptr<Task> task,Nestability nestability,const base::MutexGuard &)39 void DefaultForegroundTaskRunner::PostTaskLocked(std::unique_ptr<Task> task,
40 Nestability nestability,
41 const base::MutexGuard&) {
42 if (terminated_) return;
43 task_queue_.push_back(std::make_pair(nestability, std::move(task)));
44 event_loop_control_.NotifyOne();
45 }
46
PostTask(std::unique_ptr<Task> task)47 void DefaultForegroundTaskRunner::PostTask(std::unique_ptr<Task> task) {
48 base::MutexGuard guard(&lock_);
49 PostTaskLocked(std::move(task), kNestable, guard);
50 }
51
MonotonicallyIncreasingTime()52 double DefaultForegroundTaskRunner::MonotonicallyIncreasingTime() {
53 return time_function_();
54 }
55
PostDelayedTaskLocked(std::unique_ptr<Task> task,double delay_in_seconds,Nestability nestability,const base::MutexGuard &)56 void DefaultForegroundTaskRunner::PostDelayedTaskLocked(
57 std::unique_ptr<Task> task, double delay_in_seconds,
58 Nestability nestability, const base::MutexGuard&) {
59 DCHECK_GE(delay_in_seconds, 0.0);
60 if (terminated_) return;
61 double deadline = MonotonicallyIncreasingTime() + delay_in_seconds;
62 delayed_task_queue_.push({deadline, nestability, std::move(task)});
63 event_loop_control_.NotifyOne();
64 }
65
PostDelayedTask(std::unique_ptr<Task> task,double delay_in_seconds)66 void DefaultForegroundTaskRunner::PostDelayedTask(std::unique_ptr<Task> task,
67 double delay_in_seconds) {
68 base::MutexGuard guard(&lock_);
69 PostDelayedTaskLocked(std::move(task), delay_in_seconds, kNestable, guard);
70 }
71
PostNonNestableDelayedTask(std::unique_ptr<Task> task,double delay_in_seconds)72 void DefaultForegroundTaskRunner::PostNonNestableDelayedTask(
73 std::unique_ptr<Task> task, double delay_in_seconds) {
74 base::MutexGuard guard(&lock_);
75 PostDelayedTaskLocked(std::move(task), delay_in_seconds, kNonNestable, guard);
76 }
77
PostIdleTask(std::unique_ptr<IdleTask> task)78 void DefaultForegroundTaskRunner::PostIdleTask(std::unique_ptr<IdleTask> task) {
79 CHECK_EQ(IdleTaskSupport::kEnabled, idle_task_support_);
80 base::MutexGuard guard(&lock_);
81 if (terminated_) return;
82 idle_task_queue_.push(std::move(task));
83 }
84
IdleTasksEnabled()85 bool DefaultForegroundTaskRunner::IdleTasksEnabled() {
86 return idle_task_support_ == IdleTaskSupport::kEnabled;
87 }
88
PostNonNestableTask(std::unique_ptr<Task> task)89 void DefaultForegroundTaskRunner::PostNonNestableTask(
90 std::unique_ptr<Task> task) {
91 base::MutexGuard guard(&lock_);
92 PostTaskLocked(std::move(task), kNonNestable, guard);
93 }
94
NonNestableTasksEnabled() const95 bool DefaultForegroundTaskRunner::NonNestableTasksEnabled() const {
96 return true;
97 }
98
HasPoppableTaskInQueue() const99 bool DefaultForegroundTaskRunner::HasPoppableTaskInQueue() const {
100 if (nesting_depth_ == 0) return !task_queue_.empty();
101 for (auto it = task_queue_.cbegin(); it != task_queue_.cend(); it++) {
102 if (it->first == kNestable) return true;
103 }
104 return false;
105 }
106
MoveExpiredDelayedTasks(const base::MutexGuard & guard)107 void DefaultForegroundTaskRunner::MoveExpiredDelayedTasks(
108 const base::MutexGuard& guard) {
109 Nestability nestability;
110 std::unique_ptr<Task> task =
111 PopTaskFromDelayedQueueLocked(guard, &nestability);
112 while (task) {
113 PostTaskLocked(std::move(task), nestability, guard);
114 task = PopTaskFromDelayedQueueLocked(guard, &nestability);
115 }
116 }
117
PopTaskFromQueue(MessageLoopBehavior wait_for_work)118 std::unique_ptr<Task> DefaultForegroundTaskRunner::PopTaskFromQueue(
119 MessageLoopBehavior wait_for_work) {
120 base::MutexGuard guard(&lock_);
121 MoveExpiredDelayedTasks(guard);
122
123 while (!HasPoppableTaskInQueue()) {
124 if (wait_for_work == MessageLoopBehavior::kDoNotWait) return {};
125 WaitForTaskLocked(guard);
126 MoveExpiredDelayedTasks(guard);
127 }
128
129 auto it = task_queue_.begin();
130 for (; it != task_queue_.end(); it++) {
131 // When the task queue is nested (i.e. popping a task from the queue from
132 // within a task), only nestable tasks may run. Otherwise, any task may run.
133 if (nesting_depth_ == 0 || it->first == kNestable) break;
134 }
135 DCHECK(it != task_queue_.end());
136 std::unique_ptr<Task> task = std::move(it->second);
137 task_queue_.erase(it);
138
139 return task;
140 }
141
142 std::unique_ptr<Task>
PopTaskFromDelayedQueueLocked(const base::MutexGuard &,Nestability * nestability)143 DefaultForegroundTaskRunner::PopTaskFromDelayedQueueLocked(
144 const base::MutexGuard&, Nestability* nestability) {
145 if (delayed_task_queue_.empty()) return {};
146
147 double now = MonotonicallyIncreasingTime();
148 const DelayedEntry& entry = delayed_task_queue_.top();
149 if (entry.timeout_time > now) return {};
150 // The const_cast here is necessary because there does not exist a clean way
151 // to get a unique_ptr out of the priority queue. We provide the priority
152 // queue with a custom comparison operator to make sure that the priority
153 // queue does not access the unique_ptr. Therefore it should be safe to reset
154 // the unique_ptr in the priority queue here. Note that the DelayedEntry is
155 // removed from the priority_queue immediately afterwards.
156 std::unique_ptr<Task> task = std::move(const_cast<DelayedEntry&>(entry).task);
157 *nestability = entry.nestability;
158 delayed_task_queue_.pop();
159 return task;
160 }
161
PopTaskFromIdleQueue()162 std::unique_ptr<IdleTask> DefaultForegroundTaskRunner::PopTaskFromIdleQueue() {
163 base::MutexGuard guard(&lock_);
164 if (idle_task_queue_.empty()) return {};
165
166 std::unique_ptr<IdleTask> task = std::move(idle_task_queue_.front());
167 idle_task_queue_.pop();
168
169 return task;
170 }
171
WaitForTaskLocked(const base::MutexGuard &)172 void DefaultForegroundTaskRunner::WaitForTaskLocked(const base::MutexGuard&) {
173 if (!delayed_task_queue_.empty()) {
174 double now = MonotonicallyIncreasingTime();
175 const DelayedEntry& entry = delayed_task_queue_.top();
176 double time_until_task = entry.timeout_time - now;
177 if (time_until_task > 0) {
178 bool woken_up = event_loop_control_.WaitFor(
179 &lock_,
180 base::TimeDelta::FromMicroseconds(
181 time_until_task * base::TimeConstants::kMicrosecondsPerSecond));
182 USE(woken_up);
183 }
184 } else {
185 event_loop_control_.Wait(&lock_);
186 }
187 }
188
189 } // namespace platform
190 } // namespace v8
191