// 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