blob: 0dd1c17674a5e2b1b19c9dfeebd9cdbad18c5b5c [file] [log] [blame]
// 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 <algorithm>
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/timer/timer.h"
#include "components/domain_reliability/util.h"
namespace domain_reliability {
struct DomainReliabilityDispatcher::Task {
Task(const base::Closure& closure,
std::unique_ptr<MockableTime::Timer> timer,
base::TimeDelta min_delay,
base::TimeDelta max_delay);
~Task();
base::Closure closure;
std::unique_ptr<MockableTime::Timer> timer;
base::TimeDelta min_delay;
base::TimeDelta max_delay;
bool eligible;
};
DomainReliabilityDispatcher::Task::Task(
const base::Closure& closure,
std::unique_ptr<MockableTime::Timer> timer,
base::TimeDelta min_delay,
base::TimeDelta max_delay)
: closure(closure),
timer(std::move(timer)),
min_delay(min_delay),
max_delay(max_delay),
eligible(false) {}
DomainReliabilityDispatcher::Task::~Task() {}
DomainReliabilityDispatcher::DomainReliabilityDispatcher(MockableTime* time)
: time_(time) {}
DomainReliabilityDispatcher::~DomainReliabilityDispatcher() {}
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);
std::unique_ptr<Task> owned_task = std::make_unique<Task>(
closure, time_->CreateTimer(), min_delay, max_delay);
Task* task = owned_task.get();
tasks_.insert(std::move(owned_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 (auto* task : tasks) {
DCHECK(task);
DCHECK(task->eligible);
RunAndDeleteTask(task);
}
}
void DomainReliabilityDispatcher::RunAllTasksForTesting() {
std::set<Task*> tasks;
for (auto& task : tasks_)
tasks.insert(task.get());
for (auto* task : tasks) {
DCHECK(task);
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);
auto it = std::find_if(tasks_.begin(), tasks_.end(),
[task](const std::unique_ptr<Task>& task_ptr) {
return task_ptr.get() == task;
});
DCHECK(it != tasks_.end());
tasks_.erase(it);
}
} // namespace domain_reliability