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
10namespace v8 {
11namespace platform {
12
13DefaultForegroundTaskRunner::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
20DefaultForegroundTaskRunner::RunTaskScope::~RunTaskScope() {
21  DCHECK_GT(task_runner_->nesting_depth_, 0);
22  task_runner_->nesting_depth_--;
23}
24
25DefaultForegroundTaskRunner::DefaultForegroundTaskRunner(
26    IdleTaskSupport idle_task_support, TimeFunction time_function)
27    : idle_task_support_(idle_task_support), time_function_(time_function) {}
28
29void 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
39void 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
47void DefaultForegroundTaskRunner::PostTask(std::unique_ptr<Task> task) {
48  base::MutexGuard guard(&lock_);
49  PostTaskLocked(std::move(task), kNestable, guard);
50}
51
52double DefaultForegroundTaskRunner::MonotonicallyIncreasingTime() {
53  return time_function_();
54}
55
56void 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
66void 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
72void 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
78void 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
85bool DefaultForegroundTaskRunner::IdleTasksEnabled() {
86  return idle_task_support_ == IdleTaskSupport::kEnabled;
87}
88
89void DefaultForegroundTaskRunner::PostNonNestableTask(
90    std::unique_ptr<Task> task) {
91  base::MutexGuard guard(&lock_);
92  PostTaskLocked(std::move(task), kNonNestable, guard);
93}
94
95bool DefaultForegroundTaskRunner::NonNestableTasksEnabled() const {
96  return true;
97}
98
99bool 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
107void 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
118std::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
142std::unique_ptr<Task>
143DefaultForegroundTaskRunner::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
162std::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
172void 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