blob: a457c288b419f7ca34820459054f7535a6996194 [file] [log] [blame]
// Copyright 2018 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 "base/task/sequence_manager/sequence_manager_impl.h"
#include <stddef.h>
#include <memory>
#include <utility>
#include "base/auto_reset.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/cancelable_callback.h"
#include "base/location.h"
#include "base/memory/ref_counted_memory.h"
#include "base/memory/scoped_refptr.h"
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_current.h"
#include "base/message_loop/message_pump_default.h"
#include "base/optional.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/sequence_manager/real_time_domain.h"
#include "base/task/sequence_manager/sequence_manager.h"
#include "base/task/sequence_manager/task_queue_impl.h"
#include "base/task/sequence_manager/task_queue_selector.h"
#include "base/task/sequence_manager/tasks.h"
#include "base/task/sequence_manager/test/mock_time_domain.h"
#include "base/task/sequence_manager/test/mock_time_message_pump.h"
#include "base/task/sequence_manager/test/sequence_manager_for_test.h"
#include "base/task/sequence_manager/test/test_task_queue.h"
#include "base/task/sequence_manager/test/test_task_time_observer.h"
#include "base/task/sequence_manager/thread_controller_with_message_pump_impl.h"
#include "base/task/sequence_manager/work_queue.h"
#include "base/task/sequence_manager/work_queue_sets.h"
#include "base/test/bind_test_util.h"
#include "base/test/mock_callback.h"
#include "base/test/null_task_runner.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/test/test_simple_task_runner.h"
#include "base/test/trace_event_analyzer.h"
#include "base/threading/thread.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "base/trace_event/blame_context.h"
#include "build/build_config.h"
#include "testing/gmock/include/gmock/gmock.h"
using base::sequence_manager::internal::EnqueueOrder;
using testing::_;
using testing::AnyNumber;
using testing::Contains;
using testing::ElementsAre;
using testing::ElementsAreArray;
using testing::HasSubstr;
using testing::Mock;
using testing::Not;
using testing::Return;
using testing::StrictMock;
using testing::UnorderedElementsAre;
namespace base {
namespace sequence_manager {
namespace internal {
// To avoid symbol collisions in jumbo builds.
namespace sequence_manager_impl_unittest {
enum class TestType {
kMockTaskRunner,
kMessageLoop,
kMessagePump,
};
std::string ToString(TestType type) {
switch (type) {
case TestType::kMockTaskRunner:
return "kMockTaskRunner";
case TestType::kMessagePump:
return "kMessagePump";
case TestType::kMessageLoop:
return "kMessageLoop";
}
}
std::string GetTestNameSuffix(const testing::TestParamInfo<TestType>& info) {
return StrCat({"With", ToString(info.param).substr(1)});
}
void PrintTo(const TestType type, std::ostream* os) {
*os << ToString(type);
}
using MockTask = MockCallback<base::RepeatingCallback<void()>>;
// This class abstracts the details of how the SequenceManager runs tasks.
// Subclasses will use a MockTaskRunner, a MessageLoop or a MockMessagePump. We
// can then have common tests for all the scenarios by just using this
// interface.
class Fixture {
public:
virtual ~Fixture() = default;
virtual void AdvanceMockTickClock(TimeDelta delta) = 0;
virtual const TickClock* mock_tick_clock() const = 0;
virtual TimeDelta NextPendingTaskDelay() const = 0;
// Keeps advancing time as needed to run tasks up to the specified limit.
virtual void FastForwardBy(TimeDelta delta) = 0;
// Keeps advancing time as needed to run tasks until no more tasks are
// available.
virtual void FastForwardUntilNoTasksRemain() = 0;
virtual void RunDoWorkOnce() = 0;
virtual SequenceManagerForTest* sequence_manager() const = 0;
virtual void DestroySequenceManager() = 0;
virtual int GetNowTicksCallCount() = 0;
};
class CallCountingTickClock : public TickClock {
public:
explicit CallCountingTickClock(RepeatingCallback<TimeTicks()> now_callback)
: now_callback_(std::move(now_callback)) {}
explicit CallCountingTickClock(TickClock* clock)
: CallCountingTickClock(
BindLambdaForTesting([clock]() { return clock->NowTicks(); })) {}
~CallCountingTickClock() override = default;
TimeTicks NowTicks() const override {
++now_call_count_;
return now_callback_.Run();
}
void Reset() { now_call_count_.store(0); }
int now_call_count() const { return now_call_count_; }
private:
const RepeatingCallback<TimeTicks()> now_callback_;
mutable std::atomic<int> now_call_count_{0};
};
class FixtureWithMockTaskRunner final : public Fixture {
public:
FixtureWithMockTaskRunner()
: test_task_runner_(MakeRefCounted<TestMockTimeTaskRunner>(
TestMockTimeTaskRunner::Type::kBoundToThread)),
call_counting_clock_(BindRepeating(&TestMockTimeTaskRunner::NowTicks,
test_task_runner_)),
sequence_manager_(SequenceManagerForTest::Create(
nullptr,
ThreadTaskRunnerHandle::Get(),
mock_tick_clock(),
SequenceManager::Settings::Builder()
.SetMessageLoopType(MessageLoop::Type::TYPE_DEFAULT)
.SetRandomisedSamplingEnabled(false)
.SetTickClock(mock_tick_clock())
.Build())) {
// A null clock triggers some assertions.
AdvanceMockTickClock(TimeDelta::FromMilliseconds(1));
// The SequenceManager constructor calls Now() once for setting up
// housekeeping.
EXPECT_EQ(1, GetNowTicksCallCount());
call_counting_clock_.Reset();
}
void AdvanceMockTickClock(TimeDelta delta) override {
test_task_runner_->AdvanceMockTickClock(delta);
}
const TickClock* mock_tick_clock() const override {
return &call_counting_clock_;
}
TimeDelta NextPendingTaskDelay() const override {
return test_task_runner_->NextPendingTaskDelay();
}
void FastForwardBy(TimeDelta delta) override {
test_task_runner_->FastForwardBy(delta);
}
void FastForwardUntilNoTasksRemain() override {
test_task_runner_->FastForwardUntilNoTasksRemain();
}
void RunDoWorkOnce() override {
EXPECT_EQ(test_task_runner_->GetPendingTaskCount(), 1u);
// We should only run tasks already posted by that moment.
RunLoop run_loop;
test_task_runner_->PostTask(FROM_HERE, run_loop.QuitClosure());
// TestMockTimeTaskRunner will fast-forward mock clock if necessary.
run_loop.Run();
}
scoped_refptr<TestMockTimeTaskRunner> test_task_runner() const {
return test_task_runner_;
}
SequenceManagerForTest* sequence_manager() const override {
return sequence_manager_.get();
}
void DestroySequenceManager() override { sequence_manager_.reset(); }
int GetNowTicksCallCount() override {
return call_counting_clock_.now_call_count();
}
private:
scoped_refptr<TestMockTimeTaskRunner> test_task_runner_;
CallCountingTickClock call_counting_clock_;
std::unique_ptr<SequenceManagerForTest> sequence_manager_;
};
class FixtureWithMockMessagePump : public Fixture {
public:
FixtureWithMockMessagePump() : call_counting_clock_(&mock_clock_) {
// A null clock triggers some assertions.
mock_clock_.Advance(TimeDelta::FromMilliseconds(1));
auto pump = std::make_unique<MockTimeMessagePump>(&mock_clock_);
pump_ = pump.get();
auto settings = SequenceManager::Settings::Builder()
.SetMessageLoopType(MessageLoop::Type::TYPE_DEFAULT)
.SetRandomisedSamplingEnabled(false)
.SetTickClock(mock_tick_clock())
.Build();
sequence_manager_ = SequenceManagerForTest::Create(
std::make_unique<ThreadControllerWithMessagePumpImpl>(std::move(pump),
settings),
std::move(settings));
sequence_manager_->SetDefaultTaskRunner(MakeRefCounted<NullTaskRunner>());
// The SequenceManager constructor calls Now() once for setting up
// housekeeping.
EXPECT_EQ(1, GetNowTicksCallCount());
call_counting_clock_.Reset();
}
void AdvanceMockTickClock(TimeDelta delta) override {
mock_clock_.Advance(delta);
}
const TickClock* mock_tick_clock() const override {
return &call_counting_clock_;
}
TimeDelta NextPendingTaskDelay() const override {
return pump_->next_wake_up_time() - mock_tick_clock()->NowTicks();
}
void FastForwardBy(TimeDelta delta) override {
pump_->SetAllowTimeToAutoAdvanceUntil(mock_tick_clock()->NowTicks() +
delta);
pump_->SetStopWhenMessagePumpIsIdle(true);
RunLoop().Run();
pump_->SetStopWhenMessagePumpIsIdle(false);
}
void FastForwardUntilNoTasksRemain() override {
pump_->SetAllowTimeToAutoAdvanceUntil(TimeTicks::Max());
pump_->SetStopWhenMessagePumpIsIdle(true);
RunLoop().Run();
pump_->SetStopWhenMessagePumpIsIdle(false);
pump_->SetAllowTimeToAutoAdvanceUntil(mock_tick_clock()->NowTicks());
}
void RunDoWorkOnce() override {
pump_->SetQuitAfterDoSomeWork(true);
RunLoop().Run();
pump_->SetQuitAfterDoSomeWork(false);
}
SequenceManagerForTest* sequence_manager() const override {
return sequence_manager_.get();
}
void DestroySequenceManager() override {
pump_ = nullptr;
sequence_manager_.reset();
}
int GetNowTicksCallCount() override {
return call_counting_clock_.now_call_count();
}
private:
MockTimeMessagePump* pump_ = nullptr;
SimpleTestTickClock mock_clock_;
CallCountingTickClock call_counting_clock_;
std::unique_ptr<SequenceManagerForTest> sequence_manager_;
};
class FixtureWithMessageLoop : public Fixture {
public:
FixtureWithMessageLoop()
: call_counting_clock_(&mock_clock_),
auto_reset_global_clock_(&global_clock_, &call_counting_clock_) {
// A null clock triggers some assertions.
mock_clock_.Advance(TimeDelta::FromMilliseconds(1));
scoped_clock_override_ =
std::make_unique<base::subtle::ScopedTimeClockOverrides>(
nullptr, TicksNowOverride, nullptr);
auto pump = std::make_unique<MockTimeMessagePump>(&mock_clock_);
pump_ = pump.get();
message_loop_ = std::make_unique<MessageLoop>(std::move(pump));
sequence_manager_ = SequenceManagerForTest::CreateOnCurrentThread(
SequenceManager::Settings::Builder()
.SetMessageLoopType(MessageLoop::Type::TYPE_DEFAULT)
.SetRandomisedSamplingEnabled(false)
.SetTickClock(mock_tick_clock())
.Build());
// The SequenceManager constructor calls Now() once for setting up
// housekeeping. The MessageLoop also contains a SequenceManager so two
// calls are expected.
EXPECT_EQ(2, GetNowTicksCallCount());
call_counting_clock_.Reset();
}
void AdvanceMockTickClock(TimeDelta delta) override {
mock_clock_.Advance(delta);
}
const TickClock* mock_tick_clock() const override {
return &call_counting_clock_;
}
TimeDelta NextPendingTaskDelay() const override {
return pump_->next_wake_up_time() - mock_tick_clock()->NowTicks();
}
void FastForwardBy(TimeDelta delta) override {
pump_->SetAllowTimeToAutoAdvanceUntil(mock_tick_clock()->NowTicks() +
delta);
pump_->SetStopWhenMessagePumpIsIdle(true);
RunLoop().Run();
pump_->SetStopWhenMessagePumpIsIdle(false);
}
void FastForwardUntilNoTasksRemain() override {
pump_->SetAllowTimeToAutoAdvanceUntil(TimeTicks::Max());
pump_->SetStopWhenMessagePumpIsIdle(true);
RunLoop().Run();
pump_->SetStopWhenMessagePumpIsIdle(false);
pump_->SetAllowTimeToAutoAdvanceUntil(mock_tick_clock()->NowTicks());
}
void RunDoWorkOnce() override {
pump_->SetQuitAfterDoSomeWork(true);
RunLoop().Run();
pump_->SetQuitAfterDoSomeWork(false);
}
SequenceManagerForTest* sequence_manager() const override {
return sequence_manager_.get();
}
void DestroySequenceManager() override {
pump_ = nullptr;
sequence_manager_.reset();
}
int GetNowTicksCallCount() override {
return call_counting_clock_.now_call_count();
}
private:
static TickClock* global_clock_;
static TimeTicks TicksNowOverride() { return global_clock_->NowTicks(); }
SimpleTestTickClock mock_clock_;
CallCountingTickClock call_counting_clock_;
AutoReset<TickClock*> auto_reset_global_clock_;
std::unique_ptr<base::subtle::ScopedTimeClockOverrides>
scoped_clock_override_;
std::unique_ptr<MessageLoop> message_loop_;
MockTimeMessagePump* pump_ = nullptr;
std::unique_ptr<SequenceManagerForTest> sequence_manager_;
};
TickClock* FixtureWithMessageLoop::global_clock_;
// Convenience wrapper around the fixtures so that we can use parametrized tests
// instead of templated ones. The latter would be more verbose as all method
// calls to the fixture would need to be like this->method()
class SequenceManagerTest : public testing::TestWithParam<TestType>,
public Fixture {
public:
SequenceManagerTest() {
switch (GetParam()) {
case TestType::kMockTaskRunner:
fixture_ = std::make_unique<FixtureWithMockTaskRunner>();
break;
case TestType::kMessagePump:
fixture_ = std::make_unique<FixtureWithMockMessagePump>();
break;
case TestType::kMessageLoop:
fixture_ = std::make_unique<FixtureWithMessageLoop>();
break;
default:
NOTREACHED();
}
}
scoped_refptr<TestTaskQueue> CreateTaskQueue(
TaskQueue::Spec spec = TaskQueue::Spec("test")) {
return sequence_manager()->CreateTaskQueueWithType<TestTaskQueue>(spec);
}
std::vector<scoped_refptr<TestTaskQueue>> CreateTaskQueues(
size_t num_queues) {
std::vector<scoped_refptr<TestTaskQueue>> queues;
for (size_t i = 0; i < num_queues; i++)
queues.push_back(CreateTaskQueue());
return queues;
}
void RunUntilManagerIsIdle(RepeatingClosure per_run_time_callback) {
for (;;) {
// Advance time if we've run out of immediate work to do.
if (!sequence_manager()->HasImmediateWork()) {
LazyNow lazy_now(mock_tick_clock());
Optional<TimeDelta> delay =
sequence_manager()->GetRealTimeDomain()->DelayTillNextTask(
&lazy_now);
if (delay) {
AdvanceMockTickClock(*delay);
per_run_time_callback.Run();
} else {
break;
}
}
RunLoop().RunUntilIdle();
}
}
void AdvanceMockTickClock(TimeDelta delta) override {
fixture_->AdvanceMockTickClock(delta);
}
const TickClock* mock_tick_clock() const override {
return fixture_->mock_tick_clock();
}
TimeDelta NextPendingTaskDelay() const override {
return fixture_->NextPendingTaskDelay();
}
void FastForwardBy(TimeDelta delta) override {
fixture_->FastForwardBy(delta);
}
void FastForwardUntilNoTasksRemain() override {
fixture_->FastForwardUntilNoTasksRemain();
}
void RunDoWorkOnce() override { fixture_->RunDoWorkOnce(); }
SequenceManagerForTest* sequence_manager() const override {
return fixture_->sequence_manager();
}
void DestroySequenceManager() override { fixture_->DestroySequenceManager(); }
int GetNowTicksCallCount() override {
return fixture_->GetNowTicksCallCount();
}
private:
std::unique_ptr<Fixture> fixture_;
};
INSTANTIATE_TEST_SUITE_P(,
SequenceManagerTest,
testing::Values(TestType::kMockTaskRunner,
TestType::kMessageLoop,
TestType::kMessagePump),
GetTestNameSuffix);
void PostFromNestedRunloop(scoped_refptr<TestTaskQueue> runner,
std::vector<std::pair<OnceClosure, bool>>* tasks) {
for (std::pair<OnceClosure, bool>& pair : *tasks) {
if (pair.second) {
runner->task_runner()->PostTask(FROM_HERE, std::move(pair.first));
} else {
runner->task_runner()->PostNonNestableTask(FROM_HERE,
std::move(pair.first));
}
}
RunLoop(RunLoop::Type::kNestableTasksAllowed).RunUntilIdle();
}
void NopTask() {}
class TestCountUsesTimeSource : public TickClock {
public:
TestCountUsesTimeSource() = default;
~TestCountUsesTimeSource() override = default;
TimeTicks NowTicks() const override {
now_calls_count_++;
// Don't return 0, as it triggers some assertions.
return TimeTicks() + TimeDelta::FromSeconds(1);
}
int now_calls_count() const { return now_calls_count_; }
private:
mutable int now_calls_count_ = 0;
DISALLOW_COPY_AND_ASSIGN(TestCountUsesTimeSource);
};
TEST_P(SequenceManagerTest, NowNotCalledIfUnneeded) {
sequence_manager()->SetWorkBatchSize(6);
auto queues = CreateTaskQueues(3u);
queues[0]->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
queues[0]->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
queues[1]->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
queues[1]->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
queues[2]->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
queues[2]->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
RunLoop().RunUntilIdle();
EXPECT_EQ(0, GetNowTicksCallCount());
}
TEST_P(SequenceManagerTest,
NowCalledMinimumNumberOfTimesToComputeTaskDurations) {
TestTaskTimeObserver time_observer;
sequence_manager()->SetWorkBatchSize(6);
sequence_manager()->AddTaskTimeObserver(&time_observer);
auto queues = CreateTaskQueues(3u);
queues[0]->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
queues[0]->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
queues[1]->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
queues[1]->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
queues[2]->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
queues[2]->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
RunLoop().RunUntilIdle();
// Now is called when each task starts running and when its completed.
// 6 * 2 = 12 calls.
EXPECT_EQ(12, GetNowTicksCallCount());
}
TEST_P(SequenceManagerTest,
NowCalledMinimumNumberOfTimesToComputeTaskDurationsDelayedFenceAllowed) {
TestTaskTimeObserver time_observer;
sequence_manager()->SetWorkBatchSize(6);
sequence_manager()->AddTaskTimeObserver(&time_observer);
std::vector<scoped_refptr<TestTaskQueue>> queues;
for (size_t i = 0; i < 3; i++) {
queues.push_back(
CreateTaskQueue(TaskQueue::Spec("test").SetDelayedFencesAllowed(true)));
}
queues[0]->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
queues[0]->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
queues[1]->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
queues[1]->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
queues[2]->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
queues[2]->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
RunLoop().RunUntilIdle();
// Now is called each time a task is queued, when first task is started
// running, and when a task is completed. 6 * 3 = 18 calls.
EXPECT_EQ(18, GetNowTicksCallCount());
}
void NullTask() {}
void TestTask(uint64_t value, std::vector<EnqueueOrder>* out_result) {
out_result->push_back(EnqueueOrder::FromIntForTesting(value));
}
void DisableQueueTestTask(uint64_t value,
std::vector<EnqueueOrder>* out_result,
TaskQueue::QueueEnabledVoter* voter) {
out_result->push_back(EnqueueOrder::FromIntForTesting(value));
voter->SetVoteToEnable(false);
}
TEST_P(SequenceManagerTest, SingleQueuePosting) {
auto queue = CreateTaskQueue();
std::vector<EnqueueOrder> run_order;
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 1, &run_order));
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 2, &run_order));
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 3, &run_order));
RunLoop().RunUntilIdle();
EXPECT_THAT(run_order, ElementsAre(1u, 2u, 3u));
}
TEST_P(SequenceManagerTest, MultiQueuePosting) {
auto queues = CreateTaskQueues(3u);
std::vector<EnqueueOrder> run_order;
queues[0]->task_runner()->PostTask(FROM_HERE,
BindOnce(&TestTask, 1, &run_order));
queues[0]->task_runner()->PostTask(FROM_HERE,
BindOnce(&TestTask, 2, &run_order));
queues[1]->task_runner()->PostTask(FROM_HERE,
BindOnce(&TestTask, 3, &run_order));
queues[1]->task_runner()->PostTask(FROM_HERE,
BindOnce(&TestTask, 4, &run_order));
queues[2]->task_runner()->PostTask(FROM_HERE,
BindOnce(&TestTask, 5, &run_order));
queues[2]->task_runner()->PostTask(FROM_HERE,
BindOnce(&TestTask, 6, &run_order));
RunLoop().RunUntilIdle();
EXPECT_THAT(run_order, ElementsAre(1u, 2u, 3u, 4u, 5u, 6u));
}
TEST_P(SequenceManagerTest, NonNestableTaskPosting) {
auto queue = CreateTaskQueue();
std::vector<EnqueueOrder> run_order;
queue->task_runner()->PostNonNestableTask(FROM_HERE,
BindOnce(&TestTask, 1, &run_order));
RunLoop().RunUntilIdle();
EXPECT_THAT(run_order, ElementsAre(1u));
}
TEST_P(SequenceManagerTest, NonNestableTaskExecutesInExpectedOrder) {
auto queue = CreateTaskQueue();
std::vector<EnqueueOrder> run_order;
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 1, &run_order));
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 2, &run_order));
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 3, &run_order));
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 4, &run_order));
queue->task_runner()->PostNonNestableTask(FROM_HERE,
BindOnce(&TestTask, 5, &run_order));
RunLoop().RunUntilIdle();
EXPECT_THAT(run_order, ElementsAre(1u, 2u, 3u, 4u, 5u));
}
TEST_P(SequenceManagerTest, NonNestableTasksDoesntExecuteInNestedLoop) {
if (GetParam() == TestType::kMockTaskRunner)
return;
auto queue = CreateTaskQueue();
std::vector<EnqueueOrder> run_order;
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 1, &run_order));
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 2, &run_order));
std::vector<std::pair<OnceClosure, bool>> tasks_to_post_from_nested_loop;
tasks_to_post_from_nested_loop.push_back(
std::make_pair(BindOnce(&TestTask, 3, &run_order), false));
tasks_to_post_from_nested_loop.push_back(
std::make_pair(BindOnce(&TestTask, 4, &run_order), false));
tasks_to_post_from_nested_loop.push_back(
std::make_pair(BindOnce(&TestTask, 5, &run_order), true));
tasks_to_post_from_nested_loop.push_back(
std::make_pair(BindOnce(&TestTask, 6, &run_order), true));
queue->task_runner()->PostTask(
FROM_HERE, BindOnce(&PostFromNestedRunloop, queue,
Unretained(&tasks_to_post_from_nested_loop)));
RunLoop().RunUntilIdle();
// Note we expect tasks 3 & 4 to run last because they're non-nestable.
EXPECT_THAT(run_order, ElementsAre(1u, 2u, 5u, 6u, 3u, 4u));
}
namespace {
void InsertFenceAndPostTestTask(int id,
std::vector<EnqueueOrder>* run_order,
scoped_refptr<TestTaskQueue> task_queue,
SequenceManagerForTest* manager) {
run_order->push_back(EnqueueOrder::FromIntForTesting(id));
task_queue->InsertFence(TaskQueue::InsertFencePosition::kNow);
task_queue->task_runner()->PostTask(FROM_HERE,
BindOnce(&TestTask, id + 1, run_order));
// Force reload of immediate work queue. In real life the same effect can be
// achieved with cross-thread posting.
manager->ReloadEmptyWorkQueues();
}
} // namespace
TEST_P(SequenceManagerTest, TaskQueueDisabledFromNestedLoop) {
if (GetParam() == TestType::kMockTaskRunner)
return;
auto queue = CreateTaskQueue();
std::vector<EnqueueOrder> run_order;
std::vector<std::pair<OnceClosure, bool>> tasks_to_post_from_nested_loop;
tasks_to_post_from_nested_loop.push_back(
std::make_pair(BindOnce(&TestTask, 1, &run_order), false));
tasks_to_post_from_nested_loop.push_back(
std::make_pair(BindOnce(&InsertFenceAndPostTestTask, 2, &run_order, queue,
sequence_manager()),
true));
queue->task_runner()->PostTask(
FROM_HERE, BindOnce(&PostFromNestedRunloop, queue,
Unretained(&tasks_to_post_from_nested_loop)));
RunLoop().RunUntilIdle();
// Task 1 shouldn't run first due to it being non-nestable and queue gets
// blocked after task 2. Task 1 runs after existing nested message loop
// due to being posted before inserting a fence.
// This test checks that breaks when nestable task is pushed into a redo
// queue.
EXPECT_THAT(run_order, ElementsAre(2u, 1u));
queue->RemoveFence();
RunLoop().RunUntilIdle();
EXPECT_THAT(run_order, ElementsAre(2u, 1u, 3u));
}
TEST_P(SequenceManagerTest, HasPendingImmediateWork_ImmediateTask) {
auto queue = CreateTaskQueue();
std::vector<EnqueueOrder> run_order;
EXPECT_FALSE(queue->HasTaskToRunImmediately());
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 1, &run_order));
EXPECT_TRUE(queue->HasTaskToRunImmediately());
// Move the task into the |immediate_work_queue|.
EXPECT_TRUE(queue->GetTaskQueueImpl()->immediate_work_queue()->Empty());
std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
queue->CreateQueueEnabledVoter();
voter->SetVoteToEnable(false);
RunLoop().RunUntilIdle();
EXPECT_FALSE(queue->GetTaskQueueImpl()->immediate_work_queue()->Empty());
EXPECT_TRUE(queue->HasTaskToRunImmediately());
// Run the task, making the queue empty.
voter->SetVoteToEnable(true);
RunLoop().RunUntilIdle();
EXPECT_FALSE(queue->HasTaskToRunImmediately());
}
TEST_P(SequenceManagerTest, HasPendingImmediateWork_DelayedTask) {
auto queue = CreateTaskQueue();
std::vector<EnqueueOrder> run_order;
TimeDelta delay(TimeDelta::FromMilliseconds(10));
queue->task_runner()->PostDelayedTask(
FROM_HERE, BindOnce(&TestTask, 1, &run_order), delay);
EXPECT_FALSE(queue->HasTaskToRunImmediately());
AdvanceMockTickClock(delay);
EXPECT_TRUE(queue->HasTaskToRunImmediately());
// Move the task into the |delayed_work_queue|.
LazyNow lazy_now(mock_tick_clock());
sequence_manager()->MoveReadyDelayedTasksToWorkQueues(&lazy_now);
sequence_manager()->ScheduleWork();
EXPECT_FALSE(queue->GetTaskQueueImpl()->delayed_work_queue()->Empty());
EXPECT_TRUE(queue->HasTaskToRunImmediately());
// Run the task, making the queue empty.
RunLoop().RunUntilIdle();
EXPECT_TRUE(queue->GetTaskQueueImpl()->delayed_work_queue()->Empty());
}
TEST_P(SequenceManagerTest, DelayedTaskPosting) {
auto queue = CreateTaskQueue();
std::vector<EnqueueOrder> run_order;
TimeDelta delay(TimeDelta::FromMilliseconds(10));
queue->task_runner()->PostDelayedTask(
FROM_HERE, BindOnce(&TestTask, 1, &run_order), delay);
EXPECT_EQ(TimeDelta::FromMilliseconds(10), NextPendingTaskDelay());
EXPECT_FALSE(queue->HasTaskToRunImmediately());
EXPECT_TRUE(run_order.empty());
// The task doesn't run before the delay has completed.
FastForwardBy(TimeDelta::FromMilliseconds(9));
EXPECT_TRUE(run_order.empty());
// After the delay has completed, the task runs normally.
FastForwardBy(TimeDelta::FromMilliseconds(1));
EXPECT_THAT(run_order, ElementsAre(1u));
EXPECT_FALSE(queue->HasTaskToRunImmediately());
}
TEST(SequenceManagerTestWithMockTaskRunner,
DelayedTaskExecutedInOneMessageLoopTask) {
FixtureWithMockTaskRunner fixture;
auto queue =
fixture.sequence_manager()->CreateTaskQueue(TaskQueue::Spec("test"));
queue->task_runner()->PostDelayedTask(FROM_HERE, BindOnce(&NopTask),
TimeDelta::FromMilliseconds(10));
RunLoop().RunUntilIdle();
EXPECT_EQ(1u, fixture.test_task_runner()->GetPendingTaskCount());
fixture.FastForwardUntilNoTasksRemain();
EXPECT_EQ(0u, fixture.test_task_runner()->GetPendingTaskCount());
}
TEST_P(SequenceManagerTest, DelayedTaskPosting_MultipleTasks_DecendingOrder) {
auto queue = CreateTaskQueue();
std::vector<EnqueueOrder> run_order;
queue->task_runner()->PostDelayedTask(FROM_HERE,
BindOnce(&TestTask, 1, &run_order),
TimeDelta::FromMilliseconds(10));
queue->task_runner()->PostDelayedTask(FROM_HERE,
BindOnce(&TestTask, 2, &run_order),
TimeDelta::FromMilliseconds(8));
queue->task_runner()->PostDelayedTask(FROM_HERE,
BindOnce(&TestTask, 3, &run_order),
TimeDelta::FromMilliseconds(5));
EXPECT_EQ(TimeDelta::FromMilliseconds(5), NextPendingTaskDelay());
FastForwardBy(TimeDelta::FromMilliseconds(5));
EXPECT_THAT(run_order, ElementsAre(3u));
EXPECT_EQ(TimeDelta::FromMilliseconds(3), NextPendingTaskDelay());
FastForwardBy(TimeDelta::FromMilliseconds(3));
EXPECT_THAT(run_order, ElementsAre(3u, 2u));
EXPECT_EQ(TimeDelta::FromMilliseconds(2), NextPendingTaskDelay());
FastForwardBy(TimeDelta::FromMilliseconds(2));
EXPECT_THAT(run_order, ElementsAre(3u, 2u, 1u));
}
TEST_P(SequenceManagerTest, DelayedTaskPosting_MultipleTasks_AscendingOrder) {
auto queue = CreateTaskQueue();
std::vector<EnqueueOrder> run_order;
queue->task_runner()->PostDelayedTask(FROM_HERE,
BindOnce(&TestTask, 1, &run_order),
TimeDelta::FromMilliseconds(1));
queue->task_runner()->PostDelayedTask(FROM_HERE,
BindOnce(&TestTask, 2, &run_order),
TimeDelta::FromMilliseconds(5));
queue->task_runner()->PostDelayedTask(FROM_HERE,
BindOnce(&TestTask, 3, &run_order),
TimeDelta::FromMilliseconds(10));
EXPECT_EQ(TimeDelta::FromMilliseconds(1), NextPendingTaskDelay());
FastForwardBy(TimeDelta::FromMilliseconds(1));
EXPECT_THAT(run_order, ElementsAre(1u));
EXPECT_EQ(TimeDelta::FromMilliseconds(4), NextPendingTaskDelay());
FastForwardBy(TimeDelta::FromMilliseconds(4));
EXPECT_THAT(run_order, ElementsAre(1u, 2u));
EXPECT_EQ(TimeDelta::FromMilliseconds(5), NextPendingTaskDelay());
FastForwardBy(TimeDelta::FromMilliseconds(5));
EXPECT_THAT(run_order, ElementsAre(1u, 2u, 3u));
}
TEST(SequenceManagerTestWithMockTaskRunner,
PostDelayedTask_SharesUnderlyingDelayedTasks) {
FixtureWithMockTaskRunner fixture;
auto queue =
fixture.sequence_manager()->CreateTaskQueue(TaskQueue::Spec("test"));
std::vector<EnqueueOrder> run_order;
TimeDelta delay(TimeDelta::FromMilliseconds(10));
queue->task_runner()->PostDelayedTask(
FROM_HERE, BindOnce(&TestTask, 1, &run_order), delay);
queue->task_runner()->PostDelayedTask(
FROM_HERE, BindOnce(&TestTask, 2, &run_order), delay);
queue->task_runner()->PostDelayedTask(
FROM_HERE, BindOnce(&TestTask, 3, &run_order), delay);
EXPECT_EQ(1u, fixture.test_task_runner()->GetPendingTaskCount());
}
TEST(SequenceManagerTestWithMockTaskRunner,
CrossThreadTaskPostingToDisabledQueueDoesntScheduleWork) {
FixtureWithMockTaskRunner fixture;
auto queue =
fixture.sequence_manager()->CreateTaskQueue(TaskQueue::Spec("test"));
std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
queue->CreateQueueEnabledVoter();
voter->SetVoteToEnable(false);
WaitableEvent done_event;
Thread thread("TestThread");
thread.Start();
thread.task_runner()->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
// Should not schedule a DoWork.
queue->task_runner()->PostTask(
FROM_HERE, BindOnce(&NopTask));
done_event.Signal();
}));
done_event.Wait();
thread.Stop();
EXPECT_EQ(0u, fixture.test_task_runner()->GetPendingTaskCount());
// But if the queue becomes re-enabled it does schedule work.
voter->SetVoteToEnable(true);
EXPECT_EQ(1u, fixture.test_task_runner()->GetPendingTaskCount());
}
TEST(SequenceManagerTestWithMockTaskRunner,
CrossThreadTaskPostingToBlockedQueueDoesntScheduleWork) {
FixtureWithMockTaskRunner fixture;
auto queue =
fixture.sequence_manager()->CreateTaskQueue(TaskQueue::Spec("test"));
queue->InsertFence(TaskQueue::InsertFencePosition::kNow);
WaitableEvent done_event;
Thread thread("TestThread");
thread.Start();
thread.task_runner()->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
// Should not schedule a DoWork.
queue->task_runner()->PostTask(
FROM_HERE, BindOnce(&NopTask));
done_event.Signal();
}));
done_event.Wait();
thread.Stop();
EXPECT_EQ(0u, fixture.test_task_runner()->GetPendingTaskCount());
// But if the queue becomes unblocked it does schedule work.
queue->RemoveFence();
EXPECT_EQ(1u, fixture.test_task_runner()->GetPendingTaskCount());
}
class TestObject {
public:
~TestObject() { destructor_count__++; }
void Run() { FAIL() << "TestObject::Run should not be called"; }
static int destructor_count__;
};
int TestObject::destructor_count__ = 0;
TEST_P(SequenceManagerTest, PendingDelayedTasksRemovedOnShutdown) {
auto queue = CreateTaskQueue();
TestObject::destructor_count__ = 0;
TimeDelta delay(TimeDelta::FromMilliseconds(10));
queue->task_runner()->PostDelayedTask(
FROM_HERE, BindOnce(&TestObject::Run, Owned(new TestObject())), delay);
queue->task_runner()->PostTask(
FROM_HERE, BindOnce(&TestObject::Run, Owned(new TestObject())));
DestroySequenceManager();
EXPECT_EQ(2, TestObject::destructor_count__);
}
TEST_P(SequenceManagerTest, InsertAndRemoveFence) {
auto queue = CreateTaskQueue();
StrictMock<MockTask> task;
// Posting a task when pumping is disabled doesn't result in work getting
// posted.
queue->InsertFence(TaskQueue::InsertFencePosition::kNow);
queue->task_runner()->PostTask(FROM_HERE, task.Get());
EXPECT_CALL(task, Run).Times(0);
RunLoop().RunUntilIdle();
// However polling still works.
EXPECT_TRUE(queue->HasTaskToRunImmediately());
// After removing the fence the task runs normally.
queue->RemoveFence();
EXPECT_CALL(task, Run);
RunLoop().RunUntilIdle();
}
TEST_P(SequenceManagerTest, RemovingFenceForDisabledQueueDoesNotPostDoWork) {
auto queue = CreateTaskQueue();
StrictMock<MockTask> task;
std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
queue->CreateQueueEnabledVoter();
voter->SetVoteToEnable(false);
queue->InsertFence(TaskQueue::InsertFencePosition::kNow);
queue->task_runner()->PostTask(FROM_HERE, task.Get());
queue->RemoveFence();
EXPECT_CALL(task, Run).Times(0);
RunLoop().RunUntilIdle();
}
TEST_P(SequenceManagerTest, EnablingFencedQueueDoesNotPostDoWork) {
auto queue = CreateTaskQueue();
StrictMock<MockTask> task;
std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
queue->CreateQueueEnabledVoter();
voter->SetVoteToEnable(false);
queue->InsertFence(TaskQueue::InsertFencePosition::kNow);
queue->task_runner()->PostTask(FROM_HERE, task.Get());
voter->SetVoteToEnable(true);
EXPECT_CALL(task, Run).Times(0);
RunLoop().RunUntilIdle();
}
TEST_P(SequenceManagerTest, DenyRunning_BeforePosting) {
auto queue = CreateTaskQueue();
StrictMock<MockTask> task;
std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
queue->CreateQueueEnabledVoter();
voter->SetVoteToEnable(false);
queue->task_runner()->PostTask(FROM_HERE, task.Get());
EXPECT_CALL(task, Run).Times(0);
RunLoop().RunUntilIdle();
voter->SetVoteToEnable(true);
EXPECT_CALL(task, Run);
RunLoop().RunUntilIdle();
}
TEST_P(SequenceManagerTest, DenyRunning_AfterPosting) {
auto queue = CreateTaskQueue();
StrictMock<MockTask> task;
queue->task_runner()->PostTask(FROM_HERE, task.Get());
std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
queue->CreateQueueEnabledVoter();
voter->SetVoteToEnable(false);
EXPECT_CALL(task, Run).Times(0);
RunLoop().RunUntilIdle();
voter->SetVoteToEnable(true);
EXPECT_CALL(task, Run);
RunLoop().RunUntilIdle();
}
TEST_P(SequenceManagerTest, DenyRunning_AfterRemovingFence) {
auto queue = CreateTaskQueue();
std::vector<EnqueueOrder> run_order;
queue->InsertFence(TaskQueue::InsertFencePosition::kNow);
std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
queue->CreateQueueEnabledVoter();
voter->SetVoteToEnable(false);
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 1, &run_order));
RunLoop().RunUntilIdle();
EXPECT_TRUE(run_order.empty());
queue->RemoveFence();
voter->SetVoteToEnable(true);
RunLoop().RunUntilIdle();
EXPECT_THAT(run_order, ElementsAre(1u));
}
TEST_P(SequenceManagerTest, RemovingFenceWithDelayedTask) {
TimeDelta kDelay = TimeDelta::FromMilliseconds(10);
auto queue = CreateTaskQueue();
StrictMock<MockTask> task;
// Posting a delayed task when fenced will apply the delay, but won't cause
// work to executed afterwards.
queue->InsertFence(TaskQueue::InsertFencePosition::kNow);
queue->task_runner()->PostDelayedTask(FROM_HERE, task.Get(), kDelay);
// The task does not run even though it's delay is up.
EXPECT_CALL(task, Run).Times(0);
FastForwardBy(kDelay);
// Removing the fence causes the task to run.
queue->RemoveFence();
EXPECT_CALL(task, Run);
RunLoop().RunUntilIdle();
}
TEST_P(SequenceManagerTest, RemovingFenceWithMultipleDelayedTasks) {
auto queue = CreateTaskQueue();
queue->InsertFence(TaskQueue::InsertFencePosition::kNow);
std::vector<EnqueueOrder> run_order;
// Posting a delayed task when fenced will apply the delay, but won't cause
// work to executed afterwards.
TimeDelta delay1(TimeDelta::FromMilliseconds(1));
TimeDelta delay2(TimeDelta::FromMilliseconds(10));
TimeDelta delay3(TimeDelta::FromMilliseconds(20));
queue->task_runner()->PostDelayedTask(
FROM_HERE, BindOnce(&TestTask, 1, &run_order), delay1);
queue->task_runner()->PostDelayedTask(
FROM_HERE, BindOnce(&TestTask, 2, &run_order), delay2);
queue->task_runner()->PostDelayedTask(
FROM_HERE, BindOnce(&TestTask, 3, &run_order), delay3);
AdvanceMockTickClock(TimeDelta::FromMilliseconds(15));
RunLoop().RunUntilIdle();
EXPECT_TRUE(run_order.empty());
// Removing the fence causes the ready tasks to run.
queue->RemoveFence();
RunLoop().RunUntilIdle();
EXPECT_THAT(run_order, ElementsAre(1u, 2u));
}
TEST_P(SequenceManagerTest, InsertFencePreventsDelayedTasksFromRunning) {
auto queue = CreateTaskQueue();
queue->InsertFence(TaskQueue::InsertFencePosition::kNow);
std::vector<EnqueueOrder> run_order;
TimeDelta delay(TimeDelta::FromMilliseconds(10));
queue->task_runner()->PostDelayedTask(
FROM_HERE, BindOnce(&TestTask, 1, &run_order), delay);
FastForwardBy(TimeDelta::FromMilliseconds(10));
EXPECT_TRUE(run_order.empty());
}
TEST_P(SequenceManagerTest, MultipleFences) {
auto queue = CreateTaskQueue();
std::vector<EnqueueOrder> run_order;
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 1, &run_order));
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 2, &run_order));
queue->InsertFence(TaskQueue::InsertFencePosition::kNow);
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 3, &run_order));
RunLoop().RunUntilIdle();
EXPECT_THAT(run_order, ElementsAre(1u, 2u));
queue->InsertFence(TaskQueue::InsertFencePosition::kNow);
// Subsequent tasks should be blocked.
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 4, &run_order));
RunLoop().RunUntilIdle();
EXPECT_THAT(run_order, ElementsAre(1u, 2u, 3u));
}
TEST_P(SequenceManagerTest, InsertFenceThenImmediatlyRemoveDoesNotBlock) {
auto queue = CreateTaskQueue();
queue->InsertFence(TaskQueue::InsertFencePosition::kNow);
queue->RemoveFence();
std::vector<EnqueueOrder> run_order;
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 1, &run_order));
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 2, &run_order));
RunLoop().RunUntilIdle();
EXPECT_THAT(run_order, ElementsAre(1u, 2u));
}
TEST_P(SequenceManagerTest, InsertFencePostThenRemoveDoesNotBlock) {
auto queue = CreateTaskQueue();
queue->InsertFence(TaskQueue::InsertFencePosition::kNow);
std::vector<EnqueueOrder> run_order;
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 1, &run_order));
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 2, &run_order));
queue->RemoveFence();
RunLoop().RunUntilIdle();
EXPECT_THAT(run_order, ElementsAre(1u, 2u));
}
TEST_P(SequenceManagerTest, MultipleFencesWithInitiallyEmptyQueue) {
auto queue = CreateTaskQueue();
queue->InsertFence(TaskQueue::InsertFencePosition::kNow);
std::vector<EnqueueOrder> run_order;
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 1, &run_order));
queue->InsertFence(TaskQueue::InsertFencePosition::kNow);
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 2, &run_order));
RunLoop().RunUntilIdle();
EXPECT_THAT(run_order, ElementsAre(1u));
}
TEST_P(SequenceManagerTest, BlockedByFence) {
auto queue = CreateTaskQueue();
EXPECT_FALSE(queue->BlockedByFence());
queue->InsertFence(TaskQueue::InsertFencePosition::kNow);
EXPECT_TRUE(queue->BlockedByFence());
queue->RemoveFence();
EXPECT_FALSE(queue->BlockedByFence());
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
queue->InsertFence(TaskQueue::InsertFencePosition::kNow);
EXPECT_FALSE(queue->BlockedByFence());
RunLoop().RunUntilIdle();
EXPECT_TRUE(queue->BlockedByFence());
queue->RemoveFence();
EXPECT_FALSE(queue->BlockedByFence());
}
TEST_P(SequenceManagerTest, BlockedByFence_BothTypesOfFence) {
auto queue = CreateTaskQueue();
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
queue->InsertFence(TaskQueue::InsertFencePosition::kNow);
EXPECT_FALSE(queue->BlockedByFence());
queue->InsertFence(TaskQueue::InsertFencePosition::kBeginningOfTime);
EXPECT_TRUE(queue->BlockedByFence());
}
namespace {
void RecordTimeTask(std::vector<TimeTicks>* run_times, const TickClock* clock) {
run_times->push_back(clock->NowTicks());
}
void RecordTimeAndQueueTask(
std::vector<std::pair<scoped_refptr<TestTaskQueue>, TimeTicks>>* run_times,
scoped_refptr<TestTaskQueue> task_queue,
const TickClock* clock) {
run_times->emplace_back(task_queue, clock->NowTicks());
}
} // namespace
TEST_P(SequenceManagerTest, DelayedFence_DelayedTasks) {
const auto kStartTime = mock_tick_clock()->NowTicks();
scoped_refptr<TestTaskQueue> queue =
CreateTaskQueue(TaskQueue::Spec("test").SetDelayedFencesAllowed(true));
std::vector<TimeTicks> run_times;
queue->task_runner()->PostDelayedTask(
FROM_HERE, BindOnce(&RecordTimeTask, &run_times, mock_tick_clock()),
TimeDelta::FromMilliseconds(100));
queue->task_runner()->PostDelayedTask(
FROM_HERE, BindOnce(&RecordTimeTask, &run_times, mock_tick_clock()),
TimeDelta::FromMilliseconds(200));
queue->task_runner()->PostDelayedTask(
FROM_HERE, BindOnce(&RecordTimeTask, &run_times, mock_tick_clock()),
TimeDelta::FromMilliseconds(300));
queue->InsertFenceAt(mock_tick_clock()->NowTicks() +
TimeDelta::FromMilliseconds(250));
EXPECT_FALSE(queue->HasActiveFence());
FastForwardUntilNoTasksRemain();
EXPECT_TRUE(queue->HasActiveFence());
EXPECT_THAT(run_times,
ElementsAre(kStartTime + TimeDelta::FromMilliseconds(100),
kStartTime + TimeDelta::FromMilliseconds(200)));
run_times.clear();
queue->RemoveFence();
FastForwardUntilNoTasksRemain();
EXPECT_FALSE(queue->HasActiveFence());
EXPECT_THAT(run_times,
ElementsAre(kStartTime + TimeDelta::FromMilliseconds(300)));
}
TEST_P(SequenceManagerTest, DelayedFence_ImmediateTasks) {
const auto kStartTime = mock_tick_clock()->NowTicks();
scoped_refptr<TestTaskQueue> queue =
CreateTaskQueue(TaskQueue::Spec("test").SetDelayedFencesAllowed(true));
std::vector<TimeTicks> run_times;
queue->InsertFenceAt(mock_tick_clock()->NowTicks() +
TimeDelta::FromMilliseconds(250));
for (int i = 0; i < 5; ++i) {
queue->task_runner()->PostTask(
FROM_HERE, BindOnce(&RecordTimeTask, &run_times, mock_tick_clock()));
FastForwardBy(TimeDelta::FromMilliseconds(100));
if (i < 2) {
EXPECT_FALSE(queue->HasActiveFence());
} else {
EXPECT_TRUE(queue->HasActiveFence());
}
}
EXPECT_THAT(
run_times,
ElementsAre(kStartTime, kStartTime + TimeDelta::FromMilliseconds(100),
kStartTime + TimeDelta::FromMilliseconds(200)));
run_times.clear();
queue->RemoveFence();
FastForwardUntilNoTasksRemain();
EXPECT_THAT(run_times,
ElementsAre(kStartTime + TimeDelta::FromMilliseconds(500),
kStartTime + TimeDelta::FromMilliseconds(500)));
}
TEST_P(SequenceManagerTest, DelayedFence_RemovedFenceDoesNotActivate) {
const auto kStartTime = mock_tick_clock()->NowTicks();
scoped_refptr<TestTaskQueue> queue =
CreateTaskQueue(TaskQueue::Spec("test").SetDelayedFencesAllowed(true));
std::vector<TimeTicks> run_times;
queue->InsertFenceAt(mock_tick_clock()->NowTicks() +
TimeDelta::FromMilliseconds(250));
for (int i = 0; i < 3; ++i) {
queue->task_runner()->PostTask(
FROM_HERE, BindOnce(&RecordTimeTask, &run_times, mock_tick_clock()));
EXPECT_FALSE(queue->HasActiveFence());
FastForwardBy(TimeDelta::FromMilliseconds(100));
}
EXPECT_TRUE(queue->HasActiveFence());
queue->RemoveFence();
for (int i = 0; i < 2; ++i) {
queue->task_runner()->PostTask(
FROM_HERE, BindOnce(&RecordTimeTask, &run_times, mock_tick_clock()));
FastForwardBy(TimeDelta::FromMilliseconds(100));
EXPECT_FALSE(queue->HasActiveFence());
}
EXPECT_THAT(
run_times,
ElementsAre(kStartTime, kStartTime + TimeDelta::FromMilliseconds(100),
kStartTime + TimeDelta::FromMilliseconds(200),
kStartTime + TimeDelta::FromMilliseconds(300),
kStartTime + TimeDelta::FromMilliseconds(400)));
}
TEST_P(SequenceManagerTest, DelayedFence_TakeIncomingImmediateQueue) {
// This test checks that everything works correctly when a work queue
// is swapped with an immediate incoming queue and a delayed fence
// is activated, forcing a different queue to become active.
const auto kStartTime = mock_tick_clock()->NowTicks();
scoped_refptr<TestTaskQueue> queue1 =
CreateTaskQueue(TaskQueue::Spec("test").SetDelayedFencesAllowed(true));
scoped_refptr<TestTaskQueue> queue2 =
CreateTaskQueue(TaskQueue::Spec("test").SetDelayedFencesAllowed(true));
std::vector<std::pair<scoped_refptr<TestTaskQueue>, TimeTicks>> run_times;
// Fence ensures that the task posted after advancing time is blocked.
queue1->InsertFenceAt(mock_tick_clock()->NowTicks() +
TimeDelta::FromMilliseconds(250));
// This task should not be blocked and should run immediately after
// advancing time at 301ms.
queue1->task_runner()->PostTask(
FROM_HERE,
BindOnce(&RecordTimeAndQueueTask, &run_times, queue1, mock_tick_clock()));
// Force reload of immediate work queue. In real life the same effect can be
// achieved with cross-thread posting.
sequence_manager()->ReloadEmptyWorkQueues();
AdvanceMockTickClock(TimeDelta::FromMilliseconds(300));
// This task should be blocked.
queue1->task_runner()->PostTask(
FROM_HERE,
BindOnce(&RecordTimeAndQueueTask, &run_times, queue1, mock_tick_clock()));
// This task on a different runner should run as expected.
queue2->task_runner()->PostTask(
FROM_HERE,
BindOnce(&RecordTimeAndQueueTask, &run_times, queue2, mock_tick_clock()));
FastForwardUntilNoTasksRemain();
EXPECT_THAT(
run_times,
ElementsAre(
std::make_pair(queue1, kStartTime + TimeDelta::FromMilliseconds(300)),
std::make_pair(queue2,
kStartTime + TimeDelta::FromMilliseconds(300))));
}
namespace {
void ReentrantTestTask(scoped_refptr<TestTaskQueue> runner,
int countdown,
std::vector<EnqueueOrder>* out_result) {
out_result->push_back(EnqueueOrder::FromIntForTesting(countdown));
if (--countdown) {
runner->task_runner()->PostTask(
FROM_HERE, BindOnce(&ReentrantTestTask, runner, countdown, out_result));
}
}
} // namespace
TEST_P(SequenceManagerTest, ReentrantPosting) {
auto queue = CreateTaskQueue();
std::vector<EnqueueOrder> run_order;
queue->task_runner()->PostTask(
FROM_HERE, BindOnce(&ReentrantTestTask, queue, 3, &run_order));
RunLoop().RunUntilIdle();
EXPECT_THAT(run_order, ElementsAre(3u, 2u, 1u));
}
namespace {
class RefCountedCallbackFactory {
public:
OnceCallback<void()> WrapCallback(OnceCallback<void()> cb) {
return BindOnce(
[](OnceCallback<void()> cb, WeakPtr<bool>) { std::move(cb).Run(); },
std::move(cb), task_references_.GetWeakPtr());
}
bool HasReferences() const { return task_references_.HasWeakPtrs(); }
private:
bool dummy_;
WeakPtrFactory<bool> task_references_{&dummy_};
};
} // namespace
TEST_P(SequenceManagerTest, NoTasksAfterShutdown) {
auto queue = CreateTaskQueue();
StrictMock<MockTask> task;
RefCountedCallbackFactory counter;
EXPECT_CALL(task, Run).Times(0);
queue->task_runner()->PostTask(FROM_HERE, counter.WrapCallback(task.Get()));
DestroySequenceManager();
queue->task_runner()->PostTask(FROM_HERE, counter.WrapCallback(task.Get()));
if (GetParam() != TestType::kMessagePump) {
RunLoop().RunUntilIdle();
}
EXPECT_FALSE(counter.HasReferences());
}
void PostTaskToRunner(scoped_refptr<TestTaskQueue> runner,
std::vector<EnqueueOrder>* run_order) {
runner->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 1, run_order));
}
TEST_P(SequenceManagerTest, PostFromThread) {
auto queue = CreateTaskQueue();
std::vector<EnqueueOrder> run_order;
Thread thread("TestThread");
thread.Start();
thread.task_runner()->PostTask(
FROM_HERE, BindOnce(&PostTaskToRunner, queue, &run_order));
thread.Stop();
RunLoop().RunUntilIdle();
EXPECT_THAT(run_order, ElementsAre(1u));
}
void RePostingTestTask(scoped_refptr<TestTaskQueue> runner, int* run_count) {
(*run_count)++;
runner->task_runner()->PostTask(
FROM_HERE,
BindOnce(&RePostingTestTask, Unretained(runner.get()), run_count));
}
TEST_P(SequenceManagerTest, DoWorkCantPostItselfMultipleTimes) {
auto queue = CreateTaskQueue();
int run_count = 0;
queue->task_runner()->PostTask(
FROM_HERE, BindOnce(&RePostingTestTask, queue, &run_count));
RunDoWorkOnce();
EXPECT_EQ(1u, sequence_manager()->GetPendingTaskCountForTesting());
EXPECT_EQ(1, run_count);
}
TEST_P(SequenceManagerTest, PostFromNestedRunloop) {
auto queue = CreateTaskQueue();
std::vector<EnqueueOrder> run_order;
std::vector<std::pair<OnceClosure, bool>> tasks_to_post_from_nested_loop;
tasks_to_post_from_nested_loop.push_back(
std::make_pair(BindOnce(&TestTask, 1, &run_order), true));
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 0, &run_order));
queue->task_runner()->PostTask(
FROM_HERE, BindOnce(&PostFromNestedRunloop, queue,
Unretained(&tasks_to_post_from_nested_loop)));
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 2, &run_order));
RunLoop().RunUntilIdle();
EXPECT_THAT(run_order, ElementsAre(0u, 2u, 1u));
}
TEST_P(SequenceManagerTest, WorkBatching) {
auto queue = CreateTaskQueue();
sequence_manager()->SetWorkBatchSize(2);
std::vector<EnqueueOrder> run_order;
for (int i = 0; i < 4; ++i) {
queue->task_runner()->PostTask(FROM_HERE,
BindOnce(&TestTask, i, &run_order));
}
// Running one task in the host message loop should cause two posted tasks
// to get executed.
RunDoWorkOnce();
EXPECT_THAT(run_order, ElementsAre(0u, 1u));
// The second task runs the remaining two posted tasks.
RunDoWorkOnce();
EXPECT_THAT(run_order, ElementsAre(0u, 1u, 2u, 3u));
}
class MockTaskObserver : public MessageLoop::TaskObserver {
public:
MOCK_METHOD1(DidProcessTask, void(const PendingTask& task));
MOCK_METHOD1(WillProcessTask, void(const PendingTask& task));
};
TEST_P(SequenceManagerTest, TaskObserverAdding) {
auto queue = CreateTaskQueue();
MockTaskObserver observer;
sequence_manager()->SetWorkBatchSize(2);
sequence_manager()->AddTaskObserver(&observer);
std::vector<EnqueueOrder> run_order;
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 1, &run_order));
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 2, &run_order));
EXPECT_CALL(observer, WillProcessTask(_)).Times(2);
EXPECT_CALL(observer, DidProcessTask(_)).Times(2);
RunLoop().RunUntilIdle();
}
TEST_P(SequenceManagerTest, TaskObserverRemoving) {
auto queue = CreateTaskQueue();
MockTaskObserver observer;
sequence_manager()->SetWorkBatchSize(2);
sequence_manager()->AddTaskObserver(&observer);
sequence_manager()->RemoveTaskObserver(&observer);
std::vector<EnqueueOrder> run_order;
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 1, &run_order));
EXPECT_CALL(observer, WillProcessTask(_)).Times(0);
EXPECT_CALL(observer, DidProcessTask(_)).Times(0);
RunLoop().RunUntilIdle();
}
void RemoveObserverTask(SequenceManagerImpl* manager,
MessageLoop::TaskObserver* observer) {
manager->RemoveTaskObserver(observer);
}
TEST_P(SequenceManagerTest, TaskObserverRemovingInsideTask) {
auto queue = CreateTaskQueue();
MockTaskObserver observer;
sequence_manager()->SetWorkBatchSize(3);
sequence_manager()->AddTaskObserver(&observer);
queue->task_runner()->PostTask(
FROM_HERE, BindOnce(&RemoveObserverTask, sequence_manager(), &observer));
EXPECT_CALL(observer, WillProcessTask(_)).Times(1);
EXPECT_CALL(observer, DidProcessTask(_)).Times(0);
RunLoop().RunUntilIdle();
}
TEST_P(SequenceManagerTest, QueueTaskObserverAdding) {
auto queues = CreateTaskQueues(2);
MockTaskObserver observer;
sequence_manager()->SetWorkBatchSize(2);
queues[0]->AddTaskObserver(&observer);
std::vector<EnqueueOrder> run_order;
queues[0]->task_runner()->PostTask(FROM_HERE,
BindOnce(&TestTask, 1, &run_order));
queues[1]->task_runner()->PostTask(FROM_HERE,
BindOnce(&TestTask, 2, &run_order));
EXPECT_CALL(observer, WillProcessTask(_)).Times(1);
EXPECT_CALL(observer, DidProcessTask(_)).Times(1);
RunLoop().RunUntilIdle();
}
TEST_P(SequenceManagerTest, QueueTaskObserverRemoving) {
auto queue = CreateTaskQueue();
MockTaskObserver observer;
sequence_manager()->SetWorkBatchSize(2);
queue->AddTaskObserver(&observer);
queue->RemoveTaskObserver(&observer);
std::vector<EnqueueOrder> run_order;
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 1, &run_order));
EXPECT_CALL(observer, WillProcessTask(_)).Times(0);
EXPECT_CALL(observer, DidProcessTask(_)).Times(0);
RunLoop().RunUntilIdle();
}
void RemoveQueueObserverTask(scoped_refptr<TestTaskQueue> queue,
MessageLoop::TaskObserver* observer) {
queue->RemoveTaskObserver(observer);
}
TEST_P(SequenceManagerTest, QueueTaskObserverRemovingInsideTask) {
auto queue = CreateTaskQueue();
MockTaskObserver observer;
queue->AddTaskObserver(&observer);
queue->task_runner()->PostTask(
FROM_HERE, BindOnce(&RemoveQueueObserverTask, queue, &observer));
EXPECT_CALL(observer, WillProcessTask(_)).Times(1);
EXPECT_CALL(observer, DidProcessTask(_)).Times(0);
RunLoop().RunUntilIdle();
}
TEST_P(SequenceManagerTest, ThreadCheckAfterTermination) {
auto queue = CreateTaskQueue();
EXPECT_TRUE(queue->task_runner()->RunsTasksInCurrentSequence());
DestroySequenceManager();
EXPECT_TRUE(queue->task_runner()->RunsTasksInCurrentSequence());
}
TEST_P(SequenceManagerTest, TimeDomain_NextScheduledRunTime) {
auto queues = CreateTaskQueues(2u);
AdvanceMockTickClock(TimeDelta::FromMicroseconds(10000));
LazyNow lazy_now_1(mock_tick_clock());
// With no delayed tasks.
EXPECT_FALSE(
sequence_manager()->GetRealTimeDomain()->DelayTillNextTask(&lazy_now_1));
// With a non-delayed task.
queues[0]->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
EXPECT_FALSE(
sequence_manager()->GetRealTimeDomain()->DelayTillNextTask(&lazy_now_1));
// With a delayed task.
TimeDelta expected_delay = TimeDelta::FromMilliseconds(50);
queues[0]->task_runner()->PostDelayedTask(FROM_HERE, BindOnce(&NopTask),
expected_delay);
EXPECT_EQ(
expected_delay,
sequence_manager()->GetRealTimeDomain()->DelayTillNextTask(&lazy_now_1));
// With another delayed task in the same queue with a longer delay.
queues[0]->task_runner()->PostDelayedTask(FROM_HERE, BindOnce(&NopTask),
TimeDelta::FromMilliseconds(100));
EXPECT_EQ(
expected_delay,
sequence_manager()->GetRealTimeDomain()->DelayTillNextTask(&lazy_now_1));
// With another delayed task in the same queue with a shorter delay.
expected_delay = TimeDelta::FromMilliseconds(20);
queues[0]->task_runner()->PostDelayedTask(FROM_HERE, BindOnce(&NopTask),
expected_delay);
EXPECT_EQ(
expected_delay,
sequence_manager()->GetRealTimeDomain()->DelayTillNextTask(&lazy_now_1));
// With another delayed task in a different queue with a shorter delay.
expected_delay = TimeDelta::FromMilliseconds(10);
queues[1]->task_runner()->PostDelayedTask(FROM_HERE, BindOnce(&NopTask),
expected_delay);
EXPECT_EQ(
expected_delay,
sequence_manager()->GetRealTimeDomain()->DelayTillNextTask(&lazy_now_1));
// Test it updates as time progresses
AdvanceMockTickClock(expected_delay);
LazyNow lazy_now_2(mock_tick_clock());
EXPECT_EQ(
TimeDelta(),
sequence_manager()->GetRealTimeDomain()->DelayTillNextTask(&lazy_now_2));
}
TEST_P(SequenceManagerTest, TimeDomain_NextScheduledRunTime_MultipleQueues) {
auto queues = CreateTaskQueues(3u);
TimeDelta delay1 = TimeDelta::FromMilliseconds(50);
TimeDelta delay2 = TimeDelta::FromMilliseconds(5);
TimeDelta delay3 = TimeDelta::FromMilliseconds(10);
queues[0]->task_runner()->PostDelayedTask(FROM_HERE, BindOnce(&NopTask),
delay1);
queues[1]->task_runner()->PostDelayedTask(FROM_HERE, BindOnce(&NopTask),
delay2);
queues[2]->task_runner()->PostDelayedTask(FROM_HERE, BindOnce(&NopTask),
delay3);
queues[0]->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
LazyNow lazy_now(mock_tick_clock());
EXPECT_EQ(delay2, sequence_manager()->GetRealTimeDomain()->DelayTillNextTask(
&lazy_now));
}
TEST(SequenceManagerWithTaskRunnerTest, DeleteSequenceManagerInsideATask) {
FixtureWithMockTaskRunner fixture;
auto queue =
fixture.sequence_manager()->CreateTaskQueue(TaskQueue::Spec("test"));
queue->task_runner()->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
fixture.DestroySequenceManager();
}));
// This should not crash, assuming DoWork detects the SequenceManager has
// been deleted.
RunLoop().RunUntilIdle();
}
TEST_P(SequenceManagerTest, GetAndClearSystemIsQuiescentBit) {
auto queues = CreateTaskQueues(3u);
scoped_refptr<TestTaskQueue> queue0 =
CreateTaskQueue(TaskQueue::Spec("test").SetShouldMonitorQuiescence(true));
scoped_refptr<TestTaskQueue> queue1 =
CreateTaskQueue(TaskQueue::Spec("test").SetShouldMonitorQuiescence(true));
scoped_refptr<TestTaskQueue> queue2 = CreateTaskQueue();
EXPECT_TRUE(sequence_manager()->GetAndClearSystemIsQuiescentBit());
queue0->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
RunLoop().RunUntilIdle();
EXPECT_FALSE(sequence_manager()->GetAndClearSystemIsQuiescentBit());
EXPECT_TRUE(sequence_manager()->GetAndClearSystemIsQuiescentBit());
queue1->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
RunLoop().RunUntilIdle();
EXPECT_FALSE(sequence_manager()->GetAndClearSystemIsQuiescentBit());
EXPECT_TRUE(sequence_manager()->GetAndClearSystemIsQuiescentBit());
queue2->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
RunLoop().RunUntilIdle();
EXPECT_TRUE(sequence_manager()->GetAndClearSystemIsQuiescentBit());
queue0->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
queue1->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
RunLoop().RunUntilIdle();
EXPECT_FALSE(sequence_manager()->GetAndClearSystemIsQuiescentBit());
EXPECT_TRUE(sequence_manager()->GetAndClearSystemIsQuiescentBit());
}
TEST_P(SequenceManagerTest, HasPendingImmediateWork) {
auto queue = CreateTaskQueue();
EXPECT_FALSE(queue->HasTaskToRunImmediately());
queue->task_runner()->PostTask(FROM_HERE, BindOnce(NullTask));
EXPECT_TRUE(queue->HasTaskToRunImmediately());
RunLoop().RunUntilIdle();
EXPECT_FALSE(queue->HasTaskToRunImmediately());
}
TEST_P(SequenceManagerTest, HasPendingImmediateWork_DelayedTasks) {
auto queue = CreateTaskQueue();
EXPECT_FALSE(queue->HasTaskToRunImmediately());
queue->task_runner()->PostDelayedTask(FROM_HERE, BindOnce(NullTask),
TimeDelta::FromMilliseconds(12));
EXPECT_FALSE(queue->HasTaskToRunImmediately());
// Move time forwards until just before the delayed task should run.
AdvanceMockTickClock(TimeDelta::FromMilliseconds(10));
LazyNow lazy_now_1(mock_tick_clock());
sequence_manager()->MoveReadyDelayedTasksToWorkQueues(&lazy_now_1);
EXPECT_FALSE(queue->HasTaskToRunImmediately());
// Force the delayed task onto the work queue.
AdvanceMockTickClock(TimeDelta::FromMilliseconds(2));
LazyNow lazy_now_2(mock_tick_clock());
sequence_manager()->MoveReadyDelayedTasksToWorkQueues(&lazy_now_2);
EXPECT_TRUE(queue->HasTaskToRunImmediately());
sequence_manager()->ScheduleWork();
RunLoop().RunUntilIdle();
EXPECT_FALSE(queue->HasTaskToRunImmediately());
}
TEST_P(SequenceManagerTest, ImmediateTasksAreNotStarvedByDelayedTasks) {
auto queue = CreateTaskQueue();
std::vector<EnqueueOrder> run_order;
constexpr auto kDelay = TimeDelta::FromMilliseconds(10);
// By posting the immediate tasks from a delayed one we make sure that the
// delayed tasks we post afterwards have a lower enqueue_order than the
// immediate ones. Thus all the delayed ones would run before the immediate
// ones if it weren't for the anti-starvation feature we are testing here.
queue->task_runner()->PostDelayedTask(
FROM_HERE, BindLambdaForTesting([&]() {
for (int i = 0; i < 9; i++) {
queue->task_runner()->PostTask(FROM_HERE,
BindOnce(&TestTask, i, &run_order));
}
}),
kDelay);
for (int i = 10; i < 19; i++) {
queue->task_runner()->PostDelayedTask(
FROM_HERE, BindOnce(&TestTask, i, &run_order), kDelay);
}
FastForwardBy(TimeDelta::FromMilliseconds(10));
// Delayed tasks are not allowed to starve out immediate work which is why
// some of the immediate tasks run out of order.
uint64_t expected_run_order[] = {10, 11, 12, 0, 13, 14, 15, 1, 16,
17, 18, 2, 3, 4, 5, 6, 7, 8};
EXPECT_THAT(run_order, ElementsAreArray(expected_run_order));
}
TEST_P(SequenceManagerTest,
DelayedTaskDoesNotSkipAHeadOfNonDelayedTask_SameQueue) {
auto queue = CreateTaskQueue();
std::vector<EnqueueOrder> run_order;
TimeDelta delay = TimeDelta::FromMilliseconds(10);
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 2, &run_order));
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 3, &run_order));
queue->task_runner()->PostDelayedTask(
FROM_HERE, BindOnce(&TestTask, 1, &run_order), delay);
AdvanceMockTickClock(delay * 2);
RunLoop().RunUntilIdle();
EXPECT_THAT(run_order, ElementsAre(2u, 3u, 1u));
}
TEST_P(SequenceManagerTest,
DelayedTaskDoesNotSkipAHeadOfNonDelayedTask_DifferentQueues) {
auto queues = CreateTaskQueues(2u);
std::vector<EnqueueOrder> run_order;
TimeDelta delay = TimeDelta::FromMilliseconds(10);
queues[1]->task_runner()->PostTask(FROM_HERE,
BindOnce(&TestTask, 2, &run_order));
queues[1]->task_runner()->PostTask(FROM_HERE,
BindOnce(&TestTask, 3, &run_order));
queues[0]->task_runner()->PostDelayedTask(
FROM_HERE, BindOnce(&TestTask, 1, &run_order), delay);
AdvanceMockTickClock(delay * 2);
RunLoop().RunUntilIdle();
EXPECT_THAT(run_order, ElementsAre(2u, 3u, 1u));
}
TEST_P(SequenceManagerTest, DelayedTaskDoesNotSkipAHeadOfShorterDelayedTask) {
auto queues = CreateTaskQueues(2u);
std::vector<EnqueueOrder> run_order;
TimeDelta delay1 = TimeDelta::FromMilliseconds(10);
TimeDelta delay2 = TimeDelta::FromMilliseconds(5);
queues[0]->task_runner()->PostDelayedTask(
FROM_HERE, BindOnce(&TestTask, 1, &run_order), delay1);
queues[1]->task_runner()->PostDelayedTask(
FROM_HERE, BindOnce(&TestTask, 2, &run_order), delay2);
AdvanceMockTickClock(delay1 * 2);
RunLoop().RunUntilIdle();
EXPECT_THAT(run_order, ElementsAre(2u, 1u));
}
void CheckIsNested(bool* is_nested) {
*is_nested = RunLoop::IsNestedOnCurrentThread();
}
void PostAndQuitFromNestedRunloop(RunLoop* run_loop,
scoped_refptr<TestTaskQueue> runner,
bool* was_nested) {
runner->task_runner()->PostTask(FROM_HERE, run_loop->QuitClosure());
runner->task_runner()->PostTask(FROM_HERE,
BindOnce(&CheckIsNested, was_nested));
run_loop->Run();
}
TEST_P(SequenceManagerTest, QuitWhileNested) {
if (GetParam() == TestType::kMockTaskRunner)
return;
// This test makes sure we don't continue running a work batch after a nested
// run loop has been exited in the middle of the batch.
auto queue = CreateTaskQueue();
sequence_manager()->SetWorkBatchSize(2);
bool was_nested = true;
RunLoop run_loop(RunLoop::Type::kNestableTasksAllowed);
queue->task_runner()->PostTask(
FROM_HERE, BindOnce(&PostAndQuitFromNestedRunloop, Unretained(&run_loop),
queue, Unretained(&was_nested)));
RunLoop().RunUntilIdle();
EXPECT_FALSE(was_nested);
}
class SequenceNumberCapturingTaskObserver : public MessageLoop::TaskObserver {
public:
// MessageLoop::TaskObserver overrides.
void WillProcessTask(const PendingTask& pending_task) override {}
void DidProcessTask(const PendingTask& pending_task) override {
sequence_numbers_.push_back(pending_task.sequence_num);
}
const std::vector<int>& sequence_numbers() const { return sequence_numbers_; }
private:
std::vector<int> sequence_numbers_;
};
TEST_P(SequenceManagerTest, SequenceNumSetWhenTaskIsPosted) {
auto queue = CreateTaskQueue();
SequenceNumberCapturingTaskObserver observer;
sequence_manager()->AddTaskObserver(&observer);
// Register four tasks that will run in reverse order.
std::vector<EnqueueOrder> run_order;
queue->task_runner()->PostDelayedTask(FROM_HERE,
BindOnce(&TestTask, 1, &run_order),
TimeDelta::FromMilliseconds(30));
queue->task_runner()->PostDelayedTask(FROM_HERE,
BindOnce(&TestTask, 2, &run_order),
TimeDelta::FromMilliseconds(20));
queue->task_runner()->PostDelayedTask(FROM_HERE,
BindOnce(&TestTask, 3, &run_order),
TimeDelta::FromMilliseconds(10));
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 4, &run_order));
FastForwardBy(TimeDelta::FromMilliseconds(40));
ASSERT_THAT(run_order, ElementsAre(4u, 3u, 2u, 1u));
// The sequence numbers are a one-based monotonically incrememting counter
// which should be set when the task is posted rather than when it's enqueued
// onto the Incoming queue. This counter starts with 2.
EXPECT_THAT(observer.sequence_numbers(), ElementsAre(5, 4, 3, 2));
sequence_manager()->RemoveTaskObserver(&observer);
}
TEST_P(SequenceManagerTest, NewTaskQueues) {
auto queue = CreateTaskQueue();
scoped_refptr<TestTaskQueue> queue1 = CreateTaskQueue();
scoped_refptr<TestTaskQueue> queue2 = CreateTaskQueue();
scoped_refptr<TestTaskQueue> queue3 = CreateTaskQueue();
ASSERT_NE(queue1, queue2);
ASSERT_NE(queue1, queue3);
ASSERT_NE(queue2, queue3);
std::vector<EnqueueOrder> run_order;
queue1->task_runner()->PostTask(FROM_HERE,
BindOnce(&TestTask, 1, &run_order));
queue2->task_runner()->PostTask(FROM_HERE,
BindOnce(&TestTask, 2, &run_order));
queue3->task_runner()->PostTask(FROM_HERE,
BindOnce(&TestTask, 3, &run_order));
RunLoop().RunUntilIdle();
EXPECT_THAT(run_order, ElementsAre(1u, 2u, 3u));
}
TEST_P(SequenceManagerTest, ShutdownTaskQueue_TaskRunnersDetaching) {
scoped_refptr<TestTaskQueue> queue = CreateTaskQueue();
scoped_refptr<SingleThreadTaskRunner> runner1 = queue->task_runner();
scoped_refptr<SingleThreadTaskRunner> runner2 = queue->CreateTaskRunner(1);
std::vector<EnqueueOrder> run_order;
EXPECT_TRUE(runner1->PostTask(FROM_HERE, BindOnce(&TestTask, 1, &run_order)));
EXPECT_TRUE(runner2->PostTask(FROM_HERE, BindOnce(&TestTask, 2, &run_order)));
queue->ShutdownTaskQueue();
EXPECT_FALSE(
runner1->PostTask(FROM_HERE, BindOnce(&TestTask, 3, &run_order)));
EXPECT_FALSE(
runner2->PostTask(FROM_HERE, BindOnce(&TestTask, 4, &run_order)));
RunLoop().RunUntilIdle();
EXPECT_THAT(run_order, ElementsAre());
}
TEST_P(SequenceManagerTest, ShutdownTaskQueue) {
auto queue = CreateTaskQueue();
scoped_refptr<TestTaskQueue> queue1 = CreateTaskQueue();
scoped_refptr<TestTaskQueue> queue2 = CreateTaskQueue();
scoped_refptr<TestTaskQueue> queue3 = CreateTaskQueue();
ASSERT_NE(queue1, queue2);
ASSERT_NE(queue1, queue3);
ASSERT_NE(queue2, queue3);
std::vector<EnqueueOrder> run_order;
queue1->task_runner()->PostTask(FROM_HERE,
BindOnce(&TestTask, 1, &run_order));
queue2->task_runner()->PostTask(FROM_HERE,
BindOnce(&TestTask, 2, &run_order));
queue3->task_runner()->PostTask(FROM_HERE,
BindOnce(&TestTask, 3, &run_order));
queue2->ShutdownTaskQueue();
RunLoop().RunUntilIdle();
EXPECT_THAT(run_order, ElementsAre(1u, 3u));
}
TEST_P(SequenceManagerTest, ShutdownTaskQueue_WithDelayedTasks) {
auto queues = CreateTaskQueues(2u);
// Register three delayed tasks
std::vector<EnqueueOrder> run_order;
queues[0]->task_runner()->PostDelayedTask(FROM_HERE,
BindOnce(&TestTask, 1, &run_order),
TimeDelta::FromMilliseconds(10));
queues[1]->task_runner()->PostDelayedTask(FROM_HERE,
BindOnce(&TestTask, 2, &run_order),
TimeDelta::FromMilliseconds(20));
queues[0]->task_runner()->PostDelayedTask(FROM_HERE,
BindOnce(&TestTask, 3, &run_order),
TimeDelta::FromMilliseconds(30));
queues[1]->ShutdownTaskQueue();
RunLoop().RunUntilIdle();
FastForwardBy(TimeDelta::FromMilliseconds(40));
ASSERT_THAT(run_order, ElementsAre(1u, 3u));
}
namespace {
void ShutdownQueue(scoped_refptr<TestTaskQueue> queue) {
queue->ShutdownTaskQueue();
}
} // namespace
TEST_P(SequenceManagerTest, ShutdownTaskQueue_InTasks) {
auto queues = CreateTaskQueues(3u);
std::vector<EnqueueOrder> run_order;
queues[0]->task_runner()->PostTask(FROM_HERE,
BindOnce(&TestTask, 1, &run_order));
queues[0]->task_runner()->PostTask(FROM_HERE,
BindOnce(&ShutdownQueue, queues[1]));
queues[0]->task_runner()->PostTask(FROM_HERE,
BindOnce(&ShutdownQueue, queues[2]));
queues[1]->task_runner()->PostTask(FROM_HERE,
BindOnce(&TestTask, 2, &run_order));
queues[2]->task_runner()->PostTask(FROM_HERE,
BindOnce(&TestTask, 3, &run_order));
RunLoop().RunUntilIdle();
ASSERT_THAT(run_order, ElementsAre(1u));
}
namespace {
class MockObserver : public SequenceManager::Observer {
public:
MOCK_METHOD0(OnTriedToExecuteBlockedTask, void());
MOCK_METHOD0(OnBeginNestedRunLoop, void());
MOCK_METHOD0(OnExitNestedRunLoop, void());
};
} // namespace
TEST_P(SequenceManagerTest, ShutdownTaskQueueInNestedLoop) {
auto queue = CreateTaskQueue();
// We retain a reference to the task queue even when the manager has deleted
// its reference.
scoped_refptr<TestTaskQueue> task_queue = CreateTaskQueue();
std::vector<bool> log;
std::vector<std::pair<OnceClosure, bool>> tasks_to_post_from_nested_loop;
// Inside a nested run loop, call task_queue->ShutdownTaskQueue, bookended
// by calls to HasOneRefTask to make sure the manager doesn't release its
// reference until the nested run loop exits.
// NB: This first HasOneRefTask is a sanity check.
tasks_to_post_from_nested_loop.push_back(
std::make_pair(BindOnce(&NopTask), true));
tasks_to_post_from_nested_loop.push_back(std::make_pair(
BindOnce(&TaskQueue::ShutdownTaskQueue, Unretained(task_queue.get())),
true));
tasks_to_post_from_nested_loop.push_back(
std::make_pair(BindOnce(&NopTask), true));
queue->task_runner()->PostTask(
FROM_HERE, BindOnce(&PostFromNestedRunloop, queue,
Unretained(&tasks_to_post_from_nested_loop)));
RunLoop().RunUntilIdle();
// Just make sure that we don't crash.
}
TEST_P(SequenceManagerTest, TimeDomainsAreIndependant) {
auto queues = CreateTaskQueues(2u);
TimeTicks start_time_ticks = sequence_manager()->NowTicks();
std::unique_ptr<MockTimeDomain> domain_a =
std::make_unique<MockTimeDomain>(start_time_ticks);
std::unique_ptr<MockTimeDomain> domain_b =
std::make_unique<MockTimeDomain>(start_time_ticks);
sequence_manager()->RegisterTimeDomain(domain_a.get());
sequence_manager()->RegisterTimeDomain(domain_b.get());
queues[0]->SetTimeDomain(domain_a.get());
queues[1]->SetTimeDomain(domain_b.get());
std::vector<EnqueueOrder> run_order;
queues[0]->task_runner()->PostDelayedTask(FROM_HERE,
BindOnce(&TestTask, 1, &run_order),
TimeDelta::FromMilliseconds(10));
queues[0]->task_runner()->PostDelayedTask(FROM_HERE,
BindOnce(&TestTask, 2, &run_order),
TimeDelta::FromMilliseconds(20));
queues[0]->task_runner()->PostDelayedTask(FROM_HERE,
BindOnce(&TestTask, 3, &run_order),
TimeDelta::FromMilliseconds(30));
queues[1]->task_runner()->PostDelayedTask(FROM_HERE,
BindOnce(&TestTask, 4, &run_order),
TimeDelta::FromMilliseconds(10));
queues[1]->task_runner()->PostDelayedTask(FROM_HERE,
BindOnce(&TestTask, 5, &run_order),
TimeDelta::FromMilliseconds(20));
queues[1]->task_runner()->PostDelayedTask(FROM_HERE,
BindOnce(&TestTask, 6, &run_order),
TimeDelta::FromMilliseconds(30));
domain_b->SetNowTicks(start_time_ticks + TimeDelta::FromMilliseconds(50));
sequence_manager()->ScheduleWork();
RunLoop().RunUntilIdle();
EXPECT_THAT(run_order, ElementsAre(4u, 5u, 6u));
domain_a->SetNowTicks(start_time_ticks + TimeDelta::FromMilliseconds(50));
sequence_manager()->ScheduleWork();
RunLoop().RunUntilIdle();
EXPECT_THAT(run_order, ElementsAre(4u, 5u, 6u, 1u, 2u, 3u));
queues[0]->ShutdownTaskQueue();
queues[1]->ShutdownTaskQueue();
sequence_manager()->UnregisterTimeDomain(domain_a.get());
sequence_manager()->UnregisterTimeDomain(domain_b.get());
}
TEST_P(SequenceManagerTest, TimeDomainMigration) {
auto queue = CreateTaskQueue();
TimeTicks start_time_ticks = sequence_manager()->NowTicks();
std::unique_ptr<MockTimeDomain> domain_a =
std::make_unique<MockTimeDomain>(start_time_ticks);
sequence_manager()->RegisterTimeDomain(domain_a.get());
queue->SetTimeDomain(domain_a.get());
std::vector<EnqueueOrder> run_order;
queue->task_runner()->PostDelayedTask(FROM_HERE,
BindOnce(&TestTask, 1, &run_order),
TimeDelta::FromMilliseconds(10));
queue->task_runner()->PostDelayedTask(FROM_HERE,
BindOnce(&TestTask, 2, &run_order),
TimeDelta::FromMilliseconds(20));
queue->task_runner()->PostDelayedTask(FROM_HERE,
BindOnce(&TestTask, 3, &run_order),
TimeDelta::FromMilliseconds(30));
queue->task_runner()->PostDelayedTask(FROM_HERE,
BindOnce(&TestTask, 4, &run_order),
TimeDelta::FromMilliseconds(40));
domain_a->SetNowTicks(start_time_ticks + TimeDelta::FromMilliseconds(20));
sequence_manager()->ScheduleWork();
RunLoop().RunUntilIdle();
EXPECT_THAT(run_order, ElementsAre(1u, 2u));
std::unique_ptr<MockTimeDomain> domain_b =
std::make_unique<MockTimeDomain>(start_time_ticks);
sequence_manager()->RegisterTimeDomain(domain_b.get());
queue->SetTimeDomain(domain_b.get());
domain_b->SetNowTicks(start_time_ticks + TimeDelta::FromMilliseconds(50));
sequence_manager()->ScheduleWork();
RunLoop().RunUntilIdle();
EXPECT_THAT(run_order, ElementsAre(1u, 2u, 3u, 4u));
queue->ShutdownTaskQueue();
sequence_manager()->UnregisterTimeDomain(domain_a.get());
sequence_manager()->UnregisterTimeDomain(domain_b.get());
}
TEST_P(SequenceManagerTest, TimeDomainMigrationWithIncomingImmediateTasks) {
auto queue = CreateTaskQueue();
TimeTicks start_time_ticks = sequence_manager()->NowTicks();
std::unique_ptr<MockTimeDomain> domain_a =
std::make_unique<MockTimeDomain>(start_time_ticks);
std::unique_ptr<MockTimeDomain> domain_b =
std::make_unique<MockTimeDomain>(start_time_ticks);
sequence_manager()->RegisterTimeDomain(domain_a.get());
sequence_manager()->RegisterTimeDomain(domain_b.get());
queue->SetTimeDomain(domain_a.get());
std::vector<EnqueueOrder> run_order;
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&TestTask, 1, &run_order));
queue->SetTimeDomain(domain_b.get());
RunLoop().RunUntilIdle();
EXPECT_THAT(run_order, ElementsAre(1u));
queue->ShutdownTaskQueue();
sequence_manager()->UnregisterTimeDomain(domain_a.get());
sequence_manager()->UnregisterTimeDomain(domain_b.get());
}
TEST_P(SequenceManagerTest,
PostDelayedTasksReverseOrderAlternatingTimeDomains) {
auto queue = CreateTaskQueue();
std::vector<EnqueueOrder> run_order;
std::unique_ptr<internal::RealTimeDomain> domain_a =
std::make_unique<internal::RealTimeDomain>();
std::unique_ptr<internal::RealTimeDomain> domain_b =
std::make_unique<internal::RealTimeDomain>();
sequence_manager()->RegisterTimeDomain(domain_a.get());
sequence_manager()->RegisterTimeDomain(domain_b.get());
queue->SetTimeDomain(domain_a.get());
queue->task_runner()->PostDelayedTask(FROM_HERE,
BindOnce(&TestTask, 1, &run_order),
TimeDelta::FromMilliseconds(40));
queue->SetTimeDomain(domain_b.get());
queue->task_runner()->PostDelayedTask(FROM_HERE,
BindOnce(&TestTask, 2, &run_order),
TimeDelta::FromMilliseconds(30));
queue->SetTimeDomain(domain_a.get());
queue->task_runner()->PostDelayedTask(FROM_HERE,
BindOnce(&TestTask, 3, &run_order),
TimeDelta::FromMilliseconds(20));
queue->SetTimeDomain(domain_b.get());
queue->task_runner()->PostDelayedTask(FROM_HERE,
BindOnce(&TestTask, 4, &run_order),
TimeDelta::FromMilliseconds(10));
FastForwardBy(TimeDelta::FromMilliseconds(40));
EXPECT_THAT(run_order, ElementsAre(4u, 3u, 2u, 1u));
queue->ShutdownTaskQueue();
sequence_manager()->UnregisterTimeDomain(domain_a.get());
sequence_manager()->UnregisterTimeDomain(domain_b.get());
}
namespace {
class MockTaskQueueObserver : public TaskQueue::Observer {
public:
~MockTaskQueueObserver() override = default;
MOCK_METHOD2(OnQueueNextWakeUpChanged, void(TaskQueue*, TimeTicks));
};
} // namespace
TEST_P(SequenceManagerTest, TaskQueueObserver_ImmediateTask) {
auto queue = CreateTaskQueue();
MockTaskQueueObserver observer;
queue->SetObserver(&observer);
// We should get a notification when a task is posted on an empty queue.
EXPECT_CALL(observer, OnQueueNextWakeUpChanged(queue.get(), _));
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
sequence_manager()->ReloadEmptyWorkQueues();
Mock::VerifyAndClearExpectations(&observer);
// But not subsequently.
EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(0);
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
sequence_manager()->ReloadEmptyWorkQueues();
Mock::VerifyAndClearExpectations(&observer);
// Unless the immediate work queue is emptied.
sequence_manager()->TakeTask();
sequence_manager()->DidRunTask();
sequence_manager()->TakeTask();
sequence_manager()->DidRunTask();
EXPECT_CALL(observer, OnQueueNextWakeUpChanged(queue.get(), _));
queue->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
sequence_manager()->ReloadEmptyWorkQueues();
Mock::VerifyAndClearExpectations(&observer);
// Tidy up.
queue->ShutdownTaskQueue();
}
TEST_P(SequenceManagerTest, TaskQueueObserver_DelayedTask) {
auto queue = CreateTaskQueue();
TimeTicks start_time = sequence_manager()->NowTicks();
TimeDelta delay10s(TimeDelta::FromSeconds(10));
TimeDelta delay100s(TimeDelta::FromSeconds(100));
TimeDelta delay1s(TimeDelta::FromSeconds(1));
MockTaskQueueObserver observer;
queue->SetObserver(&observer);
// We should get a notification when a delayed task is posted on an empty
// queue.
EXPECT_CALL(observer,
OnQueueNextWakeUpChanged(queue.get(), start_time + delay10s));
queue->task_runner()->PostDelayedTask(FROM_HERE, BindOnce(&NopTask),
delay10s);
Mock::VerifyAndClearExpectations(&observer);
// We should not get a notification for a longer delay.
EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(0);
queue->task_runner()->PostDelayedTask(FROM_HERE, BindOnce(&NopTask),
delay100s);
Mock::VerifyAndClearExpectations(&observer);
// We should get a notification for a shorter delay.
EXPECT_CALL(observer,
OnQueueNextWakeUpChanged(queue.get(), start_time + delay1s));
queue->task_runner()->PostDelayedTask(FROM_HERE, BindOnce(&NopTask), delay1s);
Mock::VerifyAndClearExpectations(&observer);
std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
queue->CreateQueueEnabledVoter();
voter->SetVoteToEnable(false);
Mock::VerifyAndClearExpectations(&observer);
// When a queue has been enabled, we may get a notification if the
// TimeDomain's next scheduled wake-up has changed.
EXPECT_CALL(observer,
OnQueueNextWakeUpChanged(queue.get(), start_time + delay1s));
voter->SetVoteToEnable(true);
Mock::VerifyAndClearExpectations(&observer);
// Tidy up.
queue->ShutdownTaskQueue();
}
TEST_P(SequenceManagerTest, TaskQueueObserver_DelayedTaskMultipleQueues) {
auto queues = CreateTaskQueues(2u);
MockTaskQueueObserver observer;
queues[0]->SetObserver(&observer);
queues[1]->SetObserver(&observer);
TimeTicks start_time = sequence_manager()->NowTicks();
TimeDelta delay1s(TimeDelta::FromSeconds(1));
TimeDelta delay10s(TimeDelta::FromSeconds(10));
EXPECT_CALL(observer,
OnQueueNextWakeUpChanged(queues[0].get(), start_time + delay1s))
.Times(1);
EXPECT_CALL(observer,
OnQueueNextWakeUpChanged(queues[1].get(), start_time + delay10s))
.Times(1);
queues[0]->task_runner()->PostDelayedTask(FROM_HERE, BindOnce(&NopTask),
delay1s);
queues[1]->task_runner()->PostDelayedTask(FROM_HERE, BindOnce(&NopTask),
delay10s);
testing::Mock::VerifyAndClearExpectations(&observer);
std::unique_ptr<TaskQueue::QueueEnabledVoter> voter0 =
queues[0]->CreateQueueEnabledVoter();
std::unique_ptr<TaskQueue::QueueEnabledVoter> voter1 =
queues[1]->CreateQueueEnabledVoter();
// Disabling a queue should not trigger a notification.
EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(0);
voter0->SetVoteToEnable(false);
Mock::VerifyAndClearExpectations(&observer);
// Re-enabling it should should also trigger a notification.
EXPECT_CALL(observer,
OnQueueNextWakeUpChanged(queues[0].get(), start_time + delay1s));
voter0->SetVoteToEnable(true);
Mock::VerifyAndClearExpectations(&observer);
// Disabling a queue should not trigger a notification.
EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(0);
voter1->SetVoteToEnable(false);
Mock::VerifyAndClearExpectations(&observer);
// Re-enabling it should should trigger a notification.
EXPECT_CALL(observer,
OnQueueNextWakeUpChanged(queues[1].get(), start_time + delay10s));
voter1->SetVoteToEnable(true);
Mock::VerifyAndClearExpectations(&observer);
// Tidy up.
EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(AnyNumber());
queues[0]->ShutdownTaskQueue();
queues[1]->ShutdownTaskQueue();
}
TEST_P(SequenceManagerTest, TaskQueueObserver_DelayedWorkWhichCanRunNow) {
// This test checks that when delayed work becomes available
// the notification still fires. This usually happens when time advances
// and task becomes available in the middle of the scheduling code.
// For this test we rely on the fact that notification dispatching code
// is the same in all conditions and just change a time domain to
// trigger notification.
auto queue = CreateTaskQueue();
TimeDelta delay1s(TimeDelta::FromSeconds(1));
TimeDelta delay10s(TimeDelta::FromSeconds(10));
MockTaskQueueObserver observer;
queue->SetObserver(&observer);
// We should get a notification when a delayed task is posted on an empty
// queue.
EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _));
queue->task_runner()->PostDelayedTask(FROM_HERE, BindOnce(&NopTask), delay1s);
Mock::VerifyAndClearExpectations(&observer);
std::unique_ptr<TimeDomain> mock_time_domain =
std::make_unique<internal::RealTimeDomain>();
sequence_manager()->RegisterTimeDomain(mock_time_domain.get());
AdvanceMockTickClock(delay10s);
EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _));
queue->SetTimeDomain(mock_time_domain.get());
Mock::VerifyAndClearExpectations(&observer);
// Tidy up.
queue->ShutdownTaskQueue();
}
class CancelableTask {
public:
explicit CancelableTask(const TickClock* clock)
: clock_(clock), weak_factory_(this) {}
void RecordTimeTask(std::vector<TimeTicks>* run_times) {
run_times->push_back(clock_->NowTicks());
}
const TickClock* clock_;
WeakPtrFactory<CancelableTask> weak_factory_;
};
TEST_P(SequenceManagerTest, TaskQueueObserver_SweepCanceledDelayedTasks) {
auto queue = CreateTaskQueue();
MockTaskQueueObserver observer;
queue->SetObserver(&observer);
TimeTicks start_time = sequence_manager()->NowTicks();
TimeDelta delay1(TimeDelta::FromSeconds(5));
TimeDelta delay2(TimeDelta::FromSeconds(10));
EXPECT_CALL(observer,
OnQueueNextWakeUpChanged(queue.get(), start_time + delay1))
.Times(1);
CancelableTask task1(mock_tick_clock());
CancelableTask task2(mock_tick_clock());
std::vector<TimeTicks> run_times;
queue->task_runner()->PostDelayedTask(
FROM_HERE,
BindOnce(&CancelableTask::RecordTimeTask,
task1.weak_factory_.GetWeakPtr(), &run_times),
delay1);
queue->task_runner()->PostDelayedTask(
FROM_HERE,
BindOnce(&CancelableTask::RecordTimeTask,
task2.weak_factory_.GetWeakPtr(), &run_times),
delay2);
task1.weak_factory_.InvalidateWeakPtrs();
// Sweeping away canceled delayed tasks should trigger a notification.
EXPECT_CALL(observer,
OnQueueNextWakeUpChanged(queue.get(), start_time + delay2))
.Times(1);
sequence_manager()->ReclaimMemory();
}
namespace {
void ChromiumRunloopInspectionTask(
scoped_refptr<TestMockTimeTaskRunner> test_task_runner) {
// We don't expect more than 1 pending task at any time.
EXPECT_GE(1u, test_task_runner->GetPendingTaskCount());
}
} // namespace
TEST(SequenceManagerTestWithMockTaskRunner,
NumberOfPendingTasksOnChromiumRunLoop) {
FixtureWithMockTaskRunner fixture;
auto queue =
fixture.sequence_manager()->CreateTaskQueue(TaskQueue::Spec("test"));
// NOTE because tasks posted to the chromiumrun loop are not cancellable, we
// will end up with a lot more tasks posted if the delayed tasks were posted
// in the reverse order.
// TODO(alexclarke): Consider talking to the message pump directly.
for (int i = 1; i < 100; i++) {
queue->task_runner()->PostDelayedTask(
FROM_HERE,
BindOnce(&ChromiumRunloopInspectionTask, fixture.test_task_runner()),
TimeDelta::FromMilliseconds(i));
}
fixture.FastForwardUntilNoTasksRemain();
}