// Copyright 2014 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 "components/domain_reliability/dispatcher.h" #include "base/bind.h" #include "base/callback.h" #include "base/message_loop/message_loop.h" #include "base/stl_util.h" #include "base/timer/timer.h" #include "components/domain_reliability/util.h" namespace domain_reliability { struct DomainReliabilityDispatcher::Task { Task(const base::Closure& closure, scoped_ptr<MockableTime::Timer> timer, base::TimeDelta min_delay, base::TimeDelta max_delay); ~Task(); base::Closure closure; scoped_ptr<MockableTime::Timer> timer; base::TimeDelta min_delay; base::TimeDelta max_delay; bool eligible; }; DomainReliabilityDispatcher::Task::Task(const base::Closure& closure, scoped_ptr<MockableTime::Timer> timer, base::TimeDelta min_delay, base::TimeDelta max_delay) : closure(closure), timer(timer.Pass()), min_delay(min_delay), max_delay(max_delay), eligible(false) {} DomainReliabilityDispatcher::Task::~Task() {} DomainReliabilityDispatcher::DomainReliabilityDispatcher(MockableTime* time) : time_(time) {} DomainReliabilityDispatcher::~DomainReliabilityDispatcher() { // TODO(ttuttle): STLElementDeleter? STLDeleteElements(&tasks_); } void DomainReliabilityDispatcher::ScheduleTask( const base::Closure& closure, base::TimeDelta min_delay, base::TimeDelta max_delay) { DCHECK(!closure.is_null()); // Would be DCHECK_LE, but you can't << a TimeDelta. DCHECK(min_delay <= max_delay); Task* task = new Task(closure, time_->CreateTimer(), min_delay, max_delay); tasks_.insert(task); if (max_delay.InMicroseconds() < 0) RunAndDeleteTask(task); else if (min_delay.InMicroseconds() < 0) MakeTaskEligible(task); else MakeTaskWaiting(task); } void DomainReliabilityDispatcher::RunEligibleTasks() { // Move all eligible tasks to a separate set so that eligible_tasks_.erase in // RunAndDeleteTask won't erase elements out from under the iterator. (Also // keeps RunEligibleTasks from running forever if a task adds a new, already- // eligible task that does the same, and so on.) std::set<Task*> tasks; tasks.swap(eligible_tasks_); for (std::set<Task*>::const_iterator it = tasks.begin(); it != tasks.end(); ++it) { Task* task = *it; DCHECK(task); DCHECK(task->eligible); RunAndDeleteTask(task); } } void DomainReliabilityDispatcher::MakeTaskWaiting(Task* task) { DCHECK(task); DCHECK(!task->eligible); DCHECK(!task->timer->IsRunning()); task->timer->Start(FROM_HERE, task->min_delay, base::Bind(&DomainReliabilityDispatcher::MakeTaskEligible, base::Unretained(this), task)); } void DomainReliabilityDispatcher::MakeTaskEligible(Task* task) { DCHECK(task); DCHECK(!task->eligible); task->eligible = true; eligible_tasks_.insert(task); task->timer->Start(FROM_HERE, task->max_delay - task->min_delay, base::Bind(&DomainReliabilityDispatcher::RunAndDeleteTask, base::Unretained(this), task)); } void DomainReliabilityDispatcher::RunAndDeleteTask(Task* task) { DCHECK(task); DCHECK(!task->closure.is_null()); task->closure.Run(); if (task->eligible) eligible_tasks_.erase(task); tasks_.erase(task); delete task; } } // namespace domain_reliability