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

media / base / fake_single_thread_task_runner.cc [blame]

// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/base/fake_single_thread_task_runner.h"

#include <utility>

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

namespace media {

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

FakeSingleThreadTaskRunner::~FakeSingleThreadTaskRunner() = default;

bool FakeSingleThreadTaskRunner::PostDelayedTask(
    const base::Location& from_here,
    base::OnceClosure 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::Microseconds(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),
                                     std::move(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), std::move(task)));
  return true;
}

bool FakeSingleThreadTaskRunner::RunsTasksInCurrentSequence() 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;

    base::OnceClosure task = std::move(it->second);
    tasks_.erase(it);
    std::move(task).Run();
  }
}

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

  while (true) {
    // 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());
      base::OnceClosure task = std::move(it->second);
      tasks_.erase(it);
      std::move(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 base::Location& from_here,
    base::OnceClosure task,
    base::TimeDelta delay) {
  return PostDelayedTask(from_here, std::move(task), delay);
}

}  // namespace media