• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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