blob: f2dcd5f09d54af8f81b8f7af4fb9f2d2855d555b [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 "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::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),
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 (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());
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) {
NOTIMPLEMENTED();
return false;
}
} // namespace media