You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
215 lines
6.8 KiB
215 lines
6.8 KiB
// Copyright (c) 2013 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/deferred_sequenced_task_runner.h"
|
|
|
|
#include "base/bind.h"
|
|
#include "base/bind_helpers.h"
|
|
#include "base/callback_forward.h"
|
|
#include "base/location.h"
|
|
#include "base/memory/ref_counted.h"
|
|
#include "base/message_loop/message_loop.h"
|
|
#include "base/run_loop.h"
|
|
#include "base/single_thread_task_runner.h"
|
|
#include "base/threading/thread.h"
|
|
#include "testing/gmock/include/gmock/gmock.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
namespace base {
|
|
namespace {
|
|
|
|
class DeferredSequencedTaskRunnerTest : public testing::Test {
|
|
public:
|
|
class ExecuteTaskOnDestructor : public RefCounted<ExecuteTaskOnDestructor> {
|
|
public:
|
|
ExecuteTaskOnDestructor(
|
|
DeferredSequencedTaskRunnerTest* executor,
|
|
int task_id)
|
|
: executor_(executor),
|
|
task_id_(task_id) {
|
|
}
|
|
private:
|
|
friend class RefCounted<ExecuteTaskOnDestructor>;
|
|
virtual ~ExecuteTaskOnDestructor() { executor_->ExecuteTask(task_id_); }
|
|
DeferredSequencedTaskRunnerTest* executor_;
|
|
int task_id_;
|
|
};
|
|
|
|
void ExecuteTask(int task_id) {
|
|
AutoLock lock(lock_);
|
|
executed_task_ids_.push_back(task_id);
|
|
}
|
|
|
|
void PostExecuteTask(int task_id) {
|
|
runner_->PostTask(FROM_HERE,
|
|
BindOnce(&DeferredSequencedTaskRunnerTest::ExecuteTask,
|
|
Unretained(this), task_id));
|
|
}
|
|
|
|
void StartRunner() {
|
|
runner_->Start();
|
|
}
|
|
|
|
void DoNothing(ExecuteTaskOnDestructor* object) {
|
|
}
|
|
|
|
protected:
|
|
DeferredSequencedTaskRunnerTest()
|
|
: loop_(),
|
|
runner_(new DeferredSequencedTaskRunner(loop_.task_runner())) {}
|
|
|
|
MessageLoop loop_;
|
|
scoped_refptr<DeferredSequencedTaskRunner> runner_;
|
|
mutable Lock lock_;
|
|
std::vector<int> executed_task_ids_;
|
|
};
|
|
|
|
TEST_F(DeferredSequencedTaskRunnerTest, Stopped) {
|
|
PostExecuteTask(1);
|
|
RunLoop().RunUntilIdle();
|
|
EXPECT_THAT(executed_task_ids_, testing::ElementsAre());
|
|
}
|
|
|
|
TEST_F(DeferredSequencedTaskRunnerTest, Start) {
|
|
StartRunner();
|
|
PostExecuteTask(1);
|
|
RunLoop().RunUntilIdle();
|
|
EXPECT_THAT(executed_task_ids_, testing::ElementsAre(1));
|
|
}
|
|
|
|
TEST_F(DeferredSequencedTaskRunnerTest, StartWithMultipleElements) {
|
|
StartRunner();
|
|
for (int i = 1; i < 5; ++i)
|
|
PostExecuteTask(i);
|
|
|
|
RunLoop().RunUntilIdle();
|
|
EXPECT_THAT(executed_task_ids_, testing::ElementsAre(1, 2, 3, 4));
|
|
}
|
|
|
|
TEST_F(DeferredSequencedTaskRunnerTest, DeferredStart) {
|
|
PostExecuteTask(1);
|
|
RunLoop().RunUntilIdle();
|
|
EXPECT_THAT(executed_task_ids_, testing::ElementsAre());
|
|
|
|
StartRunner();
|
|
RunLoop().RunUntilIdle();
|
|
EXPECT_THAT(executed_task_ids_, testing::ElementsAre(1));
|
|
|
|
PostExecuteTask(2);
|
|
RunLoop().RunUntilIdle();
|
|
EXPECT_THAT(executed_task_ids_, testing::ElementsAre(1, 2));
|
|
}
|
|
|
|
TEST_F(DeferredSequencedTaskRunnerTest, DeferredStartWithMultipleElements) {
|
|
for (int i = 1; i < 5; ++i)
|
|
PostExecuteTask(i);
|
|
RunLoop().RunUntilIdle();
|
|
EXPECT_THAT(executed_task_ids_, testing::ElementsAre());
|
|
|
|
StartRunner();
|
|
for (int i = 5; i < 9; ++i)
|
|
PostExecuteTask(i);
|
|
RunLoop().RunUntilIdle();
|
|
EXPECT_THAT(executed_task_ids_, testing::ElementsAre(1, 2, 3, 4, 5, 6, 7, 8));
|
|
}
|
|
|
|
TEST_F(DeferredSequencedTaskRunnerTest, DeferredStartWithMultipleThreads) {
|
|
{
|
|
Thread thread1("DeferredSequencedTaskRunnerTestThread1");
|
|
Thread thread2("DeferredSequencedTaskRunnerTestThread2");
|
|
thread1.Start();
|
|
thread2.Start();
|
|
for (int i = 0; i < 5; ++i) {
|
|
thread1.task_runner()->PostTask(
|
|
FROM_HERE, BindOnce(&DeferredSequencedTaskRunnerTest::PostExecuteTask,
|
|
Unretained(this), 2 * i));
|
|
thread2.task_runner()->PostTask(
|
|
FROM_HERE, BindOnce(&DeferredSequencedTaskRunnerTest::PostExecuteTask,
|
|
Unretained(this), 2 * i + 1));
|
|
if (i == 2) {
|
|
thread1.task_runner()->PostTask(
|
|
FROM_HERE, BindOnce(&DeferredSequencedTaskRunnerTest::StartRunner,
|
|
Unretained(this)));
|
|
}
|
|
}
|
|
}
|
|
|
|
RunLoop().RunUntilIdle();
|
|
EXPECT_THAT(executed_task_ids_,
|
|
testing::WhenSorted(testing::ElementsAre(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)));
|
|
}
|
|
|
|
TEST_F(DeferredSequencedTaskRunnerTest, ObjectDestructionOrder) {
|
|
{
|
|
Thread thread("DeferredSequencedTaskRunnerTestThread");
|
|
thread.Start();
|
|
runner_ = new DeferredSequencedTaskRunner(thread.task_runner());
|
|
for (int i = 0; i < 5; ++i) {
|
|
{
|
|
// Use a block to ensure that no reference to |short_lived_object|
|
|
// is kept on the main thread after it is posted to |runner_|.
|
|
scoped_refptr<ExecuteTaskOnDestructor> short_lived_object =
|
|
new ExecuteTaskOnDestructor(this, 2 * i);
|
|
runner_->PostTask(
|
|
FROM_HERE,
|
|
BindOnce(&DeferredSequencedTaskRunnerTest::DoNothing,
|
|
Unretained(this), RetainedRef(short_lived_object)));
|
|
}
|
|
// |short_lived_object| with id |2 * i| should be destroyed before the
|
|
// task |2 * i + 1| is executed.
|
|
PostExecuteTask(2 * i + 1);
|
|
}
|
|
StartRunner();
|
|
}
|
|
|
|
// All |short_lived_object| with id |2 * i| are destroyed before the task
|
|
// |2 * i + 1| is executed.
|
|
EXPECT_THAT(executed_task_ids_,
|
|
testing::ElementsAre(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));
|
|
}
|
|
|
|
void GetRunsTasksInCurrentSequence(bool* result,
|
|
scoped_refptr<SequencedTaskRunner> runner,
|
|
OnceClosure quit) {
|
|
*result = runner->RunsTasksInCurrentSequence();
|
|
std::move(quit).Run();
|
|
}
|
|
|
|
TEST_F(DeferredSequencedTaskRunnerTest, RunsTasksInCurrentSequence) {
|
|
scoped_refptr<DeferredSequencedTaskRunner> runner =
|
|
MakeRefCounted<DeferredSequencedTaskRunner>();
|
|
EXPECT_TRUE(runner->RunsTasksInCurrentSequence());
|
|
|
|
Thread thread1("DeferredSequencedTaskRunnerTestThread1");
|
|
thread1.Start();
|
|
bool runs_task_in_current_thread = true;
|
|
base::RunLoop run_loop;
|
|
thread1.task_runner()->PostTask(
|
|
FROM_HERE,
|
|
BindOnce(&GetRunsTasksInCurrentSequence, &runs_task_in_current_thread,
|
|
runner, run_loop.QuitClosure()));
|
|
run_loop.Run();
|
|
EXPECT_FALSE(runs_task_in_current_thread);
|
|
}
|
|
|
|
TEST_F(DeferredSequencedTaskRunnerTest, StartWithTaskRunner) {
|
|
scoped_refptr<DeferredSequencedTaskRunner> runner =
|
|
MakeRefCounted<DeferredSequencedTaskRunner>();
|
|
bool run_called = false;
|
|
base::RunLoop run_loop;
|
|
runner->PostTask(FROM_HERE,
|
|
BindOnce(
|
|
[](bool* run_called, base::Closure quit_closure) {
|
|
*run_called = true;
|
|
std::move(quit_closure).Run();
|
|
},
|
|
&run_called, run_loop.QuitClosure()));
|
|
runner->StartWithTaskRunner(loop_.task_runner());
|
|
run_loop.Run();
|
|
EXPECT_TRUE(run_called);
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace base
|