summaryrefslogtreecommitdiffstats
path: root/media/cast/test/fake_single_thread_task_runner.cc
blob: f587d7afdd67593eec588fcce9b7150184ee8fd1 (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
110
111
112
113
114
115
116
117
118
// 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 "media/cast/test/fake_single_thread_task_runner.h"

#include "base/location.h"
#include "base/logging.h"
#include "base/time/tick_clock.h"

namespace media {
namespace cast {
namespace test {

FakeSingleThreadTaskRunner::FakeSingleThreadTaskRunner(
    base::SimpleTestTickClock* clock)
    : clock_(clock),
      fail_on_next_task_(false) {}

FakeSingleThreadTaskRunner::~FakeSingleThreadTaskRunner() {}

bool FakeSingleThreadTaskRunner::PostDelayedTask(
    const tracked_objects::Location& from_here,
    const base::Closure& task,
    base::TimeDelta delay) {
  if (fail_on_next_task_) {
    LOG(FATAL) << "Infinite task posting loop detected.  Possibly caused by "
               << from_here.ToString() << " posting a task with delay "
               << delay.InMicroseconds() << " usec.";
  }

  CHECK_LE(base::TimeDelta(), delay);
  const base::TimeTicks run_time = clock_->NowTicks() + delay;

  // If there are one or more tasks with the exact same run time, schedule this
  // task to occur after them.  This mimics the FIFO ordering behavior when
  // scheduling delayed tasks to be run via base::MessageLoop in a
  // multi-threaded application.
  if (!tasks_.empty()) {
    const auto after_it = tasks_.lower_bound(
        TaskKey(run_time + base::TimeDelta::FromMicroseconds(1), 0));
    if (after_it != tasks_.begin()) {
      auto it = after_it;
      --it;
      if (it->first.first == run_time) {
        tasks_.insert(
            after_it /* hint */,
            std::make_pair(TaskKey(run_time, it->first.second + 1), task));
        return true;
      }
    }
  }

  // No tasks have the exact same run time, so just do a simple insert.
  tasks_.insert(std::make_pair(TaskKey(run_time, 0), task));
  return true;
}

bool FakeSingleThreadTaskRunner::RunsTasksOnCurrentThread() const {
  return true;
}

void FakeSingleThreadTaskRunner::RunTasks() {
  while (true) {
    // Run all tasks equal or older than current time.
    const auto it = tasks_.begin();
    if (it == tasks_.end())
      return;  // No more tasks.

    if (clock_->NowTicks() < it->first.first)
      return;

    const base::Closure task = it->second;
    tasks_.erase(it);
    task.Run();
  }
}

void FakeSingleThreadTaskRunner::Sleep(base::TimeDelta t) {
  CHECK_LE(base::TimeDelta(), t);
  const base::TimeTicks run_until = clock_->NowTicks() + t;

  while (1) {
    // Run up to 100000 tasks that were scheduled to run during the sleep
    // period. 100000 should be enough for everybody (see comments below).
    for (int i = 0; i < 100000; i++) {
      const auto it = tasks_.begin();
      if (it == tasks_.end() || run_until < it->first.first) {
        clock_->Advance(run_until - clock_->NowTicks());
        return;
      }

      clock_->Advance(it->first.first - clock_->NowTicks());
      const base::Closure task = it->second;
      tasks_.erase(it);
      task.Run();
    }

    // If this point is reached, there's likely some sort of case where a new
    // non-delayed task is being posted every time a task is popped and invoked
    // from the queue. If that happens, set fail_on_next_task_ to true and throw
    // an error when the next task is posted, where we might be able to identify
    // the caller causing the problem via logging.
    fail_on_next_task_ = true;
  }
}

bool FakeSingleThreadTaskRunner::PostNonNestableDelayedTask(
    const tracked_objects::Location& from_here,
    const base::Closure& task,
    base::TimeDelta delay) {
  NOTIMPLEMENTED();
  return false;
}

}  // namespace test
}  // namespace cast
}  // namespace media