summaryrefslogtreecommitdiffstats
path: root/components/domain_reliability/dispatcher.cc
blob: ceb4da09c8b5e6ca76129831eacb696cb93af012 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// 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/message_loop/message_loop.h"
#include "base/stl_util.h"
#include "base/timer/timer.h"
#include "components/domain_reliability/util.h"

namespace domain_reliability {

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);
  }
}

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() {}

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