blob: 6286d5ef4f6f267a4fcfab471d5fbea8f07a9393 [file] [log] [blame]
// Copyright 2016 The Chromium 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 "base/task_scheduler/delayed_task_manager.h"
#include <utility>
#include "base/logging.h"
#include "base/task_scheduler/scheduler_thread_pool.h"
namespace base {
namespace internal {
struct DelayedTaskManager::DelayedTask {
DelayedTask(std::unique_ptr<Task> task,
scoped_refptr<Sequence> sequence,
SchedulerWorkerThread* worker_thread,
SchedulerThreadPool* thread_pool,
uint64_t index)
: task(std::move(task)),
sequence(std::move(sequence)),
worker_thread(worker_thread),
thread_pool(thread_pool),
index(index) {}
DelayedTask(DelayedTask&& other) = default;
~DelayedTask() = default;
DelayedTask& operator=(DelayedTask&& other) = default;
// |task| will be posted to |thread_pool| with |sequence| and |worker_thread|
// when it becomes ripe for execution.
std::unique_ptr<Task> task;
scoped_refptr<Sequence> sequence;
SchedulerWorkerThread* worker_thread;
SchedulerThreadPool* thread_pool;
// Ensures that tasks that have the same |delayed_run_time| are sorted
// according to the order in which they were added to the DelayedTaskManager.
uint64_t index;
private:
DISALLOW_COPY_AND_ASSIGN(DelayedTask);
};
DelayedTaskManager::DelayedTaskManager(
const Closure& on_delayed_run_time_updated)
: on_delayed_run_time_updated_(on_delayed_run_time_updated) {
DCHECK(!on_delayed_run_time_updated_.is_null());
}
DelayedTaskManager::~DelayedTaskManager() = default;
void DelayedTaskManager::AddDelayedTask(std::unique_ptr<Task> task,
scoped_refptr<Sequence> sequence,
SchedulerWorkerThread* worker_thread,
SchedulerThreadPool* thread_pool) {
DCHECK(task);
DCHECK(sequence);
DCHECK(thread_pool);
const TimeTicks new_task_delayed_run_time = task->delayed_run_time;
TimeTicks current_delayed_run_time;
{
AutoSchedulerLock auto_lock(lock_);
if (!delayed_tasks_.empty())
current_delayed_run_time = delayed_tasks_.top().task->delayed_run_time;
delayed_tasks_.emplace(std::move(task), std::move(sequence), worker_thread,
thread_pool, ++delayed_task_index_);
}
if (current_delayed_run_time.is_null() ||
new_task_delayed_run_time < current_delayed_run_time) {
on_delayed_run_time_updated_.Run();
}
}
void DelayedTaskManager::PostReadyTasks() {
const TimeTicks now = Now();
// Move delayed tasks that are ready for execution into |ready_tasks|. Don't
// post them right away to avoid imposing an unecessary lock dependency on
// PostTaskNowHelper.
std::vector<DelayedTask> ready_tasks;
{
AutoSchedulerLock auto_lock(lock_);
while (!delayed_tasks_.empty() &&
delayed_tasks_.top().task->delayed_run_time <= now) {
// The const_cast for std::move is okay since we're immediately popping
// the task from |delayed_tasks_|. See DelayedTaskComparator::operator()
// for minor debug-check implications.
ready_tasks.push_back(
std::move(const_cast<DelayedTask&>(delayed_tasks_.top())));
delayed_tasks_.pop();
}
}
// Post delayed tasks that are ready for execution.
for (auto& delayed_task : ready_tasks) {
delayed_task.thread_pool->PostTaskWithSequenceNow(
std::move(delayed_task.task), std::move(delayed_task.sequence),
delayed_task.worker_thread);
}
}
TimeTicks DelayedTaskManager::GetDelayedRunTime() const {
AutoSchedulerLock auto_lock(lock_);
if (delayed_tasks_.empty())
return TimeTicks();
return delayed_tasks_.top().task->delayed_run_time;
}
// In std::priority_queue, the largest element is on top. Therefore, this
// comparator returns true if the delayed run time of |right| is earlier than
// the delayed run time of |left|.
bool DelayedTaskManager::DelayedTaskComparator::operator()(
const DelayedTask& left,
const DelayedTask& right) const {
#ifndef NDEBUG
// Due to STL consistency checks in Windows and const_cast'ing right before
// popping the DelayedTask, a null task can be passed to this comparator in
// Debug builds. To satisfy these consistency checks, this comparator
// considers null tasks to be the larger than anything.
DCHECK(left.task || right.task);
if (!left.task)
return false;
if (!right.task)
return true;
#else
DCHECK(left.task);
DCHECK(right.task);
#endif // NDEBUG
if (left.task->delayed_run_time > right.task->delayed_run_time)
return true;
if (left.task->delayed_run_time < right.task->delayed_run_time)
return false;
return left.index > right.index;
}
TimeTicks DelayedTaskManager::Now() const {
return TimeTicks::Now();
}
} // namespace internal
} // namespace base