|  | // 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/task_scheduler/delayed_task_manager.h" | 
|  |  | 
|  | #include <algorithm> | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/task/post_task.h" | 
|  | #include "base/task/task_scheduler/task.h" | 
|  | #include "base/task_runner.h" | 
|  |  | 
|  | namespace base { | 
|  | namespace internal { | 
|  |  | 
|  | DelayedTaskManager::DelayedTask::DelayedTask(Task task, | 
|  | PostTaskNowCallback callback) | 
|  | : task(std::move(task)), callback(std::move(callback)) {} | 
|  |  | 
|  | DelayedTaskManager::DelayedTask::DelayedTask( | 
|  | DelayedTaskManager::DelayedTask&& other) = default; | 
|  |  | 
|  | DelayedTaskManager::DelayedTask::~DelayedTask() = default; | 
|  |  | 
|  | DelayedTaskManager::DelayedTask& DelayedTaskManager::DelayedTask::operator=( | 
|  | DelayedTaskManager::DelayedTask&& other) = default; | 
|  |  | 
|  | bool DelayedTaskManager::DelayedTask::operator>( | 
|  | const DelayedTask& other) const { | 
|  | return task.delayed_run_time > other.task.delayed_run_time; | 
|  | } | 
|  |  | 
|  | bool DelayedTaskManager::DelayedTask::IsScheduled() const { | 
|  | return scheduled_; | 
|  | } | 
|  | void DelayedTaskManager::DelayedTask::SetScheduled() { | 
|  | DCHECK(!scheduled_); | 
|  | scheduled_ = true; | 
|  | } | 
|  |  | 
|  | DelayedTaskManager::DelayedTaskManager( | 
|  | std::unique_ptr<const TickClock> tick_clock) | 
|  | : process_ripe_tasks_closure_( | 
|  | BindRepeating(&DelayedTaskManager::ProcessRipeTasks, | 
|  | Unretained(this))), | 
|  | tick_clock_(std::move(tick_clock)) { | 
|  | DCHECK(tick_clock_); | 
|  | } | 
|  |  | 
|  | DelayedTaskManager::~DelayedTaskManager() = default; | 
|  |  | 
|  | void DelayedTaskManager::Start( | 
|  | scoped_refptr<TaskRunner> service_thread_task_runner) { | 
|  | DCHECK(service_thread_task_runner); | 
|  |  | 
|  | TimeTicks process_ripe_tasks_time; | 
|  | { | 
|  | AutoSchedulerLock auto_lock(queue_lock_); | 
|  | DCHECK(!service_thread_task_runner_); | 
|  | service_thread_task_runner_ = std::move(service_thread_task_runner); | 
|  | process_ripe_tasks_time = GetTimeToScheduleProcessRipeTasksLockRequired(); | 
|  | } | 
|  | ScheduleProcessRipeTasksOnServiceThread(process_ripe_tasks_time); | 
|  | } | 
|  |  | 
|  | void DelayedTaskManager::AddDelayedTask( | 
|  | Task task, | 
|  | PostTaskNowCallback post_task_now_callback) { | 
|  | DCHECK(task.task); | 
|  | DCHECK(!task.delayed_run_time.is_null()); | 
|  |  | 
|  | // Use CHECK instead of DCHECK to crash earlier. See http://crbug.com/711167 | 
|  | // for details. | 
|  | CHECK(task.task); | 
|  | TimeTicks process_ripe_tasks_time; | 
|  | { | 
|  | AutoSchedulerLock auto_lock(queue_lock_); | 
|  | delayed_task_queue_.emplace(std::move(task), | 
|  | std::move(post_task_now_callback)); | 
|  | // Not started yet. | 
|  | if (service_thread_task_runner_ == nullptr) | 
|  | return; | 
|  | process_ripe_tasks_time = GetTimeToScheduleProcessRipeTasksLockRequired(); | 
|  | } | 
|  | ScheduleProcessRipeTasksOnServiceThread(process_ripe_tasks_time); | 
|  | } | 
|  |  | 
|  | void DelayedTaskManager::ProcessRipeTasks() { | 
|  | std::vector<DelayedTask> ripe_delayed_tasks; | 
|  | TimeTicks process_ripe_tasks_time; | 
|  |  | 
|  | { | 
|  | AutoSchedulerLock auto_lock(queue_lock_); | 
|  | const TimeTicks now = tick_clock_->NowTicks(); | 
|  | while (!delayed_task_queue_.empty() && | 
|  | delayed_task_queue_.top().task.delayed_run_time <= now) { | 
|  | // The const_cast on top is okay since the DelayedTask is | 
|  | // transactionnaly being popped from |delayed_task_queue_| right after | 
|  | // and the move doesn't alter the sort order (a requirement for the | 
|  | // Windows STL's consistency debug-checks for | 
|  | // std::priority_queue::top()). | 
|  | ripe_delayed_tasks.push_back( | 
|  | std::move(const_cast<DelayedTask&>(delayed_task_queue_.top()))); | 
|  | delayed_task_queue_.pop(); | 
|  | } | 
|  | process_ripe_tasks_time = GetTimeToScheduleProcessRipeTasksLockRequired(); | 
|  | } | 
|  | ScheduleProcessRipeTasksOnServiceThread(process_ripe_tasks_time); | 
|  |  | 
|  | for (auto& delayed_task : ripe_delayed_tasks) { | 
|  | std::move(delayed_task.callback).Run(std::move(delayed_task.task)); | 
|  | } | 
|  | } | 
|  |  | 
|  | TimeTicks DelayedTaskManager::GetTimeToScheduleProcessRipeTasksLockRequired() { | 
|  | queue_lock_.AssertAcquired(); | 
|  | if (delayed_task_queue_.empty()) | 
|  | return TimeTicks::Max(); | 
|  | // The const_cast on top is okay since |IsScheduled()| and |SetScheduled()| | 
|  | // don't alter the sort order (a requirement for the Windows STL's consistency | 
|  | // debug-checks for std::priority_queue::top()) | 
|  | DelayedTask& ripest_delayed_task = | 
|  | const_cast<DelayedTask&>(delayed_task_queue_.top()); | 
|  | if (ripest_delayed_task.IsScheduled()) | 
|  | return TimeTicks::Max(); | 
|  | ripest_delayed_task.SetScheduled(); | 
|  | return ripest_delayed_task.task.delayed_run_time; | 
|  | } | 
|  |  | 
|  | void DelayedTaskManager::ScheduleProcessRipeTasksOnServiceThread( | 
|  | TimeTicks next_delayed_task_run_time) { | 
|  | DCHECK(!next_delayed_task_run_time.is_null()); | 
|  | if (next_delayed_task_run_time.is_max()) | 
|  | return; | 
|  | const TimeTicks now = tick_clock_->NowTicks(); | 
|  | TimeDelta delay = std::max(TimeDelta(), next_delayed_task_run_time - now); | 
|  | service_thread_task_runner_->PostDelayedTask( | 
|  | FROM_HERE, process_ripe_tasks_closure_, delay); | 
|  | } | 
|  |  | 
|  | }  // namespace internal | 
|  | }  // namespace base |