blob: 85487c16d6f0b06860047ac77933f9ad502160c1 [file] [log] [blame]
// 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