| // Copyright 2017 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/libplatform/default-foreground-task-runner.h" |
| |
| #include "src/base/platform/mutex.h" |
| #include "src/libplatform/default-platform.h" |
| |
| namespace v8 { |
| namespace platform { |
| |
| DefaultForegroundTaskRunner::RunTaskScope::RunTaskScope( |
| std::shared_ptr<DefaultForegroundTaskRunner> task_runner) |
| : task_runner_(task_runner) { |
| DCHECK_GE(task_runner->nesting_depth_, 0); |
| task_runner->nesting_depth_++; |
| } |
| |
| DefaultForegroundTaskRunner::RunTaskScope::~RunTaskScope() { |
| DCHECK_GT(task_runner_->nesting_depth_, 0); |
| task_runner_->nesting_depth_--; |
| } |
| |
| DefaultForegroundTaskRunner::DefaultForegroundTaskRunner( |
| IdleTaskSupport idle_task_support, TimeFunction time_function) |
| : idle_task_support_(idle_task_support), time_function_(time_function) {} |
| |
| void DefaultForegroundTaskRunner::Terminate() { |
| base::MutexGuard guard(&lock_); |
| terminated_ = true; |
| |
| // Drain the task queues. |
| while (!task_queue_.empty()) task_queue_.pop_front(); |
| while (!delayed_task_queue_.empty()) delayed_task_queue_.pop(); |
| while (!idle_task_queue_.empty()) idle_task_queue_.pop(); |
| } |
| |
| void DefaultForegroundTaskRunner::PostTaskLocked(std::unique_ptr<Task> task, |
| Nestability nestability, |
| const base::MutexGuard&) { |
| if (terminated_) return; |
| task_queue_.push_back(std::make_pair(nestability, std::move(task))); |
| event_loop_control_.NotifyOne(); |
| } |
| |
| void DefaultForegroundTaskRunner::PostTask(std::unique_ptr<Task> task) { |
| base::MutexGuard guard(&lock_); |
| PostTaskLocked(std::move(task), kNestable, guard); |
| } |
| |
| double DefaultForegroundTaskRunner::MonotonicallyIncreasingTime() { |
| return time_function_(); |
| } |
| |
| void DefaultForegroundTaskRunner::PostDelayedTaskLocked( |
| std::unique_ptr<Task> task, double delay_in_seconds, |
| Nestability nestability, const base::MutexGuard&) { |
| DCHECK_GE(delay_in_seconds, 0.0); |
| if (terminated_) return; |
| double deadline = MonotonicallyIncreasingTime() + delay_in_seconds; |
| delayed_task_queue_.push({deadline, nestability, std::move(task)}); |
| event_loop_control_.NotifyOne(); |
| } |
| |
| void DefaultForegroundTaskRunner::PostDelayedTask(std::unique_ptr<Task> task, |
| double delay_in_seconds) { |
| base::MutexGuard guard(&lock_); |
| PostDelayedTaskLocked(std::move(task), delay_in_seconds, kNestable, guard); |
| } |
| |
| void DefaultForegroundTaskRunner::PostNonNestableDelayedTask( |
| std::unique_ptr<Task> task, double delay_in_seconds) { |
| base::MutexGuard guard(&lock_); |
| PostDelayedTaskLocked(std::move(task), delay_in_seconds, kNonNestable, guard); |
| } |
| |
| void DefaultForegroundTaskRunner::PostIdleTask(std::unique_ptr<IdleTask> task) { |
| CHECK_EQ(IdleTaskSupport::kEnabled, idle_task_support_); |
| base::MutexGuard guard(&lock_); |
| if (terminated_) return; |
| idle_task_queue_.push(std::move(task)); |
| } |
| |
| bool DefaultForegroundTaskRunner::IdleTasksEnabled() { |
| return idle_task_support_ == IdleTaskSupport::kEnabled; |
| } |
| |
| void DefaultForegroundTaskRunner::PostNonNestableTask( |
| std::unique_ptr<Task> task) { |
| base::MutexGuard guard(&lock_); |
| PostTaskLocked(std::move(task), kNonNestable, guard); |
| } |
| |
| bool DefaultForegroundTaskRunner::NonNestableTasksEnabled() const { |
| return true; |
| } |
| |
| bool DefaultForegroundTaskRunner::HasPoppableTaskInQueue() const { |
| if (nesting_depth_ == 0) return !task_queue_.empty(); |
| for (auto it = task_queue_.cbegin(); it != task_queue_.cend(); it++) { |
| if (it->first == kNestable) return true; |
| } |
| return false; |
| } |
| |
| void DefaultForegroundTaskRunner::MoveExpiredDelayedTasks( |
| const base::MutexGuard& guard) { |
| Nestability nestability; |
| std::unique_ptr<Task> task = |
| PopTaskFromDelayedQueueLocked(guard, &nestability); |
| while (task) { |
| PostTaskLocked(std::move(task), nestability, guard); |
| task = PopTaskFromDelayedQueueLocked(guard, &nestability); |
| } |
| } |
| |
| std::unique_ptr<Task> DefaultForegroundTaskRunner::PopTaskFromQueue( |
| MessageLoopBehavior wait_for_work) { |
| base::MutexGuard guard(&lock_); |
| MoveExpiredDelayedTasks(guard); |
| |
| while (!HasPoppableTaskInQueue()) { |
| if (wait_for_work == MessageLoopBehavior::kDoNotWait) return {}; |
| WaitForTaskLocked(guard); |
| MoveExpiredDelayedTasks(guard); |
| } |
| |
| auto it = task_queue_.begin(); |
| for (; it != task_queue_.end(); it++) { |
| // When the task queue is nested (i.e. popping a task from the queue from |
| // within a task), only nestable tasks may run. Otherwise, any task may run. |
| if (nesting_depth_ == 0 || it->first == kNestable) break; |
| } |
| DCHECK(it != task_queue_.end()); |
| std::unique_ptr<Task> task = std::move(it->second); |
| task_queue_.erase(it); |
| |
| return task; |
| } |
| |
| std::unique_ptr<Task> |
| DefaultForegroundTaskRunner::PopTaskFromDelayedQueueLocked( |
| const base::MutexGuard&, Nestability* nestability) { |
| if (delayed_task_queue_.empty()) return {}; |
| |
| double now = MonotonicallyIncreasingTime(); |
| const DelayedEntry& entry = delayed_task_queue_.top(); |
| if (entry.timeout_time > now) return {}; |
| // The const_cast here is necessary because there does not exist a clean way |
| // to get a unique_ptr out of the priority queue. We provide the priority |
| // queue with a custom comparison operator to make sure that the priority |
| // queue does not access the unique_ptr. Therefore it should be safe to reset |
| // the unique_ptr in the priority queue here. Note that the DelayedEntry is |
| // removed from the priority_queue immediately afterwards. |
| std::unique_ptr<Task> task = std::move(const_cast<DelayedEntry&>(entry).task); |
| *nestability = entry.nestability; |
| delayed_task_queue_.pop(); |
| return task; |
| } |
| |
| std::unique_ptr<IdleTask> DefaultForegroundTaskRunner::PopTaskFromIdleQueue() { |
| base::MutexGuard guard(&lock_); |
| if (idle_task_queue_.empty()) return {}; |
| |
| std::unique_ptr<IdleTask> task = std::move(idle_task_queue_.front()); |
| idle_task_queue_.pop(); |
| |
| return task; |
| } |
| |
| void DefaultForegroundTaskRunner::WaitForTaskLocked(const base::MutexGuard&) { |
| if (!delayed_task_queue_.empty()) { |
| double now = MonotonicallyIncreasingTime(); |
| const DelayedEntry& entry = delayed_task_queue_.top(); |
| double time_until_task = entry.timeout_time - now; |
| if (time_until_task > 0) { |
| bool woken_up = event_loop_control_.WaitFor( |
| &lock_, |
| base::TimeDelta::FromMicroseconds( |
| time_until_task * base::TimeConstants::kMicrosecondsPerSecond)); |
| USE(woken_up); |
| } |
| } else { |
| event_loop_control_.Wait(&lock_); |
| } |
| } |
| |
| } // namespace platform |
| } // namespace v8 |