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