326 lines
9.8 KiB
C++
326 lines
9.8 KiB
C++
// Copyright 2020 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "base/task/bind_post_task.h"
|
|
|
|
#include "base/functional/bind.h"
|
|
#include "base/functional/callback.h"
|
|
#include "base/memory/raw_ref.h"
|
|
#include "base/sequence_checker_impl.h"
|
|
#include "base/task/sequenced_task_runner.h"
|
|
#include "base/test/task_environment.h"
|
|
#include "base/threading/thread.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
namespace base {
|
|
namespace {
|
|
|
|
void SetBool(bool* variable, bool value) {
|
|
*variable = value;
|
|
}
|
|
|
|
void SetInt(int* variable, int value) {
|
|
*variable = value;
|
|
}
|
|
|
|
void SetIntFromUniquePtr(int* variable, std::unique_ptr<int> value) {
|
|
*variable = *value;
|
|
}
|
|
|
|
int Multiply(int value) {
|
|
return value * 5;
|
|
}
|
|
|
|
void ClearReference(OnceClosure callback) {}
|
|
|
|
class SequenceRestrictionChecker {
|
|
public:
|
|
explicit SequenceRestrictionChecker(bool& set_on_destroy)
|
|
: set_on_destroy_(set_on_destroy) {}
|
|
|
|
~SequenceRestrictionChecker() {
|
|
EXPECT_TRUE(checker_.CalledOnValidSequence());
|
|
*set_on_destroy_ = true;
|
|
}
|
|
|
|
void Run() { EXPECT_TRUE(checker_.CalledOnValidSequence()); }
|
|
|
|
private:
|
|
SequenceCheckerImpl checker_;
|
|
const raw_ref<bool> set_on_destroy_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
class BindPostTaskTest : public testing::Test {
|
|
protected:
|
|
test::SingleThreadTaskEnvironment task_environment_;
|
|
scoped_refptr<SequencedTaskRunner> task_runner_ =
|
|
SequencedTaskRunner::GetCurrentDefault();
|
|
};
|
|
|
|
TEST_F(BindPostTaskTest, OnceClosure) {
|
|
bool val = false;
|
|
OnceClosure cb = BindOnce(&SetBool, &val, true);
|
|
OnceClosure post_cb = BindPostTask(task_runner_, std::move(cb));
|
|
|
|
std::move(post_cb).Run();
|
|
EXPECT_FALSE(val);
|
|
|
|
RunLoop().RunUntilIdle();
|
|
EXPECT_TRUE(val);
|
|
}
|
|
|
|
TEST_F(BindPostTaskTest, OnceCallback) {
|
|
OnceCallback<void(bool*, bool)> cb = BindOnce(&SetBool);
|
|
OnceCallback<void(bool*, bool)> post_cb =
|
|
BindPostTask(task_runner_, std::move(cb));
|
|
|
|
bool val = false;
|
|
std::move(post_cb).Run(&val, true);
|
|
EXPECT_FALSE(val);
|
|
|
|
RunLoop().RunUntilIdle();
|
|
EXPECT_TRUE(val);
|
|
}
|
|
|
|
TEST_F(BindPostTaskTest, OnceWithBoundMoveOnlyArg) {
|
|
int val = 0;
|
|
OnceClosure cb =
|
|
BindOnce(&SetIntFromUniquePtr, &val, std::make_unique<int>(10));
|
|
OnceClosure post_cb = BindPostTask(task_runner_, std::move(cb));
|
|
|
|
std::move(post_cb).Run();
|
|
EXPECT_EQ(val, 0);
|
|
|
|
RunLoop().RunUntilIdle();
|
|
EXPECT_EQ(val, 10);
|
|
}
|
|
|
|
TEST_F(BindPostTaskTest, OnceWithUnboundMoveOnlyArg) {
|
|
int val = 0;
|
|
OnceCallback<void(std::unique_ptr<int>)> cb =
|
|
BindOnce(&SetIntFromUniquePtr, &val);
|
|
OnceCallback<void(std::unique_ptr<int>)> post_cb =
|
|
BindPostTask(task_runner_, std::move(cb));
|
|
|
|
std::move(post_cb).Run(std::make_unique<int>(10));
|
|
EXPECT_EQ(val, 0);
|
|
|
|
RunLoop().RunUntilIdle();
|
|
EXPECT_EQ(val, 10);
|
|
}
|
|
|
|
TEST_F(BindPostTaskTest, OnceWithIgnoreResult) {
|
|
OnceCallback<void(int)> post_cb =
|
|
BindPostTask(task_runner_, BindOnce(IgnoreResult(&Multiply)));
|
|
std::move(post_cb).Run(1);
|
|
RunLoop().RunUntilIdle();
|
|
}
|
|
|
|
TEST_F(BindPostTaskTest, OnceThen) {
|
|
int value = 0;
|
|
|
|
// Multiply() returns an int and SetInt() takes an int as a parameter.
|
|
OnceClosure then_cb =
|
|
BindOnce(&Multiply, 5)
|
|
.Then(BindPostTask(task_runner_, BindOnce(&SetInt, &value)));
|
|
|
|
std::move(then_cb).Run();
|
|
EXPECT_EQ(value, 0);
|
|
RunLoop().RunUntilIdle();
|
|
EXPECT_EQ(value, 25);
|
|
}
|
|
|
|
// Ensure that the input callback is run/destroyed on the correct thread even if
|
|
// the callback returned from BindPostTask() is run on a different thread.
|
|
TEST_F(BindPostTaskTest, OnceRunDestroyedOnBound) {
|
|
Thread target_thread("testing");
|
|
ASSERT_TRUE(target_thread.Start());
|
|
|
|
// SequenceRestrictionChecker checks it's creation, Run() and deletion all
|
|
// happen on the main thread.
|
|
bool destroyed = false;
|
|
auto checker = std::make_unique<SequenceRestrictionChecker>(destroyed);
|
|
|
|
// `checker` is owned by `cb` which is wrapped in `post_cb`. `post_cb` is run
|
|
// on a different thread which triggers a PostTask() back to the test main
|
|
// thread to invoke `cb` which runs SequenceRestrictionChecker::Run(). After
|
|
// `cb` has been invoked `checker` is destroyed along with the BindState.
|
|
OnceClosure cb =
|
|
BindOnce(&SequenceRestrictionChecker::Run, std::move(checker));
|
|
OnceClosure post_cb = BindPostTask(task_runner_, std::move(cb));
|
|
target_thread.task_runner()->PostTask(FROM_HERE, std::move(post_cb));
|
|
|
|
target_thread.FlushForTesting();
|
|
EXPECT_FALSE(destroyed);
|
|
RunLoop().RunUntilIdle();
|
|
EXPECT_TRUE(destroyed);
|
|
}
|
|
|
|
// Ensure that the input callback is destroyed on the correct thread even if the
|
|
// callback returned from BindPostTask() is destroyed without being run on a
|
|
// different thread.
|
|
TEST_F(BindPostTaskTest, OnceNotRunDestroyedOnBound) {
|
|
Thread target_thread("testing");
|
|
ASSERT_TRUE(target_thread.Start());
|
|
|
|
// SequenceRestrictionChecker checks it's creation and deletion all happen on
|
|
// the test main thread.
|
|
bool destroyed = false;
|
|
auto checker = std::make_unique<SequenceRestrictionChecker>(destroyed);
|
|
|
|
// `checker` is owned by `cb` which is wrapped in `post_cb`. `post_cb` is
|
|
// deleted on a different thread which triggers a PostTask() back to the test
|
|
// main thread to destroy `cb` and `checker`.
|
|
OnceClosure cb =
|
|
BindOnce(&SequenceRestrictionChecker::Run, std::move(checker));
|
|
OnceClosure post_cb = BindPostTask(task_runner_, std::move(cb));
|
|
target_thread.task_runner()->PostTask(
|
|
FROM_HERE, BindOnce(&ClearReference, std::move(post_cb)));
|
|
|
|
target_thread.FlushForTesting();
|
|
EXPECT_FALSE(destroyed);
|
|
RunLoop().RunUntilIdle();
|
|
EXPECT_TRUE(destroyed);
|
|
}
|
|
|
|
TEST_F(BindPostTaskTest, RepeatingClosure) {
|
|
bool val = false;
|
|
RepeatingClosure cb = BindRepeating(&SetBool, &val, true);
|
|
RepeatingClosure post_cb = BindPostTask(task_runner_, std::move(cb));
|
|
|
|
post_cb.Run();
|
|
EXPECT_FALSE(val);
|
|
|
|
RunLoop().RunUntilIdle();
|
|
EXPECT_TRUE(val);
|
|
|
|
val = false;
|
|
post_cb.Run();
|
|
EXPECT_FALSE(val);
|
|
|
|
RunLoop().RunUntilIdle();
|
|
EXPECT_TRUE(val);
|
|
}
|
|
|
|
TEST_F(BindPostTaskTest, RepeatingCallback) {
|
|
RepeatingCallback<void(bool*, bool)> cb = BindRepeating(&SetBool);
|
|
RepeatingCallback<void(bool*, bool)> post_cb =
|
|
BindPostTask(task_runner_, std::move(cb));
|
|
|
|
bool val = false;
|
|
post_cb.Run(&val, true);
|
|
EXPECT_FALSE(val);
|
|
|
|
RunLoop().RunUntilIdle();
|
|
EXPECT_TRUE(val);
|
|
|
|
post_cb.Run(&val, false);
|
|
EXPECT_TRUE(val);
|
|
|
|
RunLoop().RunUntilIdle();
|
|
EXPECT_FALSE(val);
|
|
}
|
|
|
|
TEST_F(BindPostTaskTest, RepeatingWithUnboundMoveOnlyArg) {
|
|
int val = 0;
|
|
RepeatingCallback<void(std::unique_ptr<int>)> cb =
|
|
BindRepeating(&SetIntFromUniquePtr, &val);
|
|
RepeatingCallback<void(std::unique_ptr<int>)> post_cb =
|
|
BindPostTask(task_runner_, std::move(cb));
|
|
|
|
post_cb.Run(std::make_unique<int>(10));
|
|
EXPECT_EQ(val, 0);
|
|
|
|
RunLoop().RunUntilIdle();
|
|
EXPECT_EQ(val, 10);
|
|
|
|
post_cb.Run(std::make_unique<int>(20));
|
|
EXPECT_EQ(val, 10);
|
|
|
|
RunLoop().RunUntilIdle();
|
|
EXPECT_EQ(val, 20);
|
|
}
|
|
|
|
TEST_F(BindPostTaskTest, RepeatingWithIgnoreResult) {
|
|
RepeatingCallback<void(int)> post_cb =
|
|
BindPostTask(task_runner_, BindRepeating(IgnoreResult(&Multiply)));
|
|
std::move(post_cb).Run(1);
|
|
RunLoop().RunUntilIdle();
|
|
}
|
|
|
|
TEST_F(BindPostTaskTest, RepeatingThen) {
|
|
int value = 0;
|
|
|
|
// Multiply() returns an int and SetInt() takes an int as a parameter.
|
|
RepeatingCallback<void(int)> then_cb = BindRepeating(&Multiply).Then(
|
|
BindPostTask(task_runner_, BindRepeating(&SetInt, &value)));
|
|
|
|
then_cb.Run(5);
|
|
EXPECT_EQ(value, 0);
|
|
RunLoop().RunUntilIdle();
|
|
EXPECT_EQ(value, 25);
|
|
|
|
then_cb.Run(10);
|
|
EXPECT_EQ(value, 25);
|
|
RunLoop().RunUntilIdle();
|
|
EXPECT_EQ(value, 50);
|
|
}
|
|
|
|
// Ensure that the input callback is run/destroyed on the correct thread even if
|
|
// the callback returned from BindPostTask() is run on a different thread.
|
|
TEST_F(BindPostTaskTest, RepeatingRunDestroyedOnBound) {
|
|
Thread target_thread("testing");
|
|
ASSERT_TRUE(target_thread.Start());
|
|
|
|
// SequenceRestrictionChecker checks it's creation, Run() and deletion all
|
|
// happen on the main thread.
|
|
bool destroyed = false;
|
|
auto checker = std::make_unique<SequenceRestrictionChecker>(destroyed);
|
|
|
|
// `checker` is owned by `cb` which is wrapped in `post_cb`. `post_cb` is run
|
|
// on a different thread which triggers a PostTask() back to the test main
|
|
// thread to invoke `cb` which runs SequenceRestrictionChecker::Run(). After
|
|
// `cb` has been invoked `checker` is destroyed along with the BindState.
|
|
RepeatingClosure cb =
|
|
BindRepeating(&SequenceRestrictionChecker::Run, std::move(checker));
|
|
RepeatingClosure post_cb = BindPostTask(task_runner_, std::move(cb));
|
|
target_thread.task_runner()->PostTask(FROM_HERE, std::move(post_cb));
|
|
|
|
target_thread.FlushForTesting();
|
|
EXPECT_FALSE(destroyed);
|
|
RunLoop().RunUntilIdle();
|
|
EXPECT_TRUE(destroyed);
|
|
}
|
|
|
|
// Ensure that the input callback is destroyed on the correct thread even if the
|
|
// callback returned from BindPostTask() is destroyed without being run on a
|
|
// different thread.
|
|
TEST_F(BindPostTaskTest, RepeatingNotRunDestroyedOnBound) {
|
|
Thread target_thread("testing");
|
|
ASSERT_TRUE(target_thread.Start());
|
|
|
|
// SequenceRestrictionChecker checks it's creation and deletion all happen on
|
|
// the test main thread.
|
|
bool destroyed = false;
|
|
auto checker = std::make_unique<SequenceRestrictionChecker>(destroyed);
|
|
|
|
// `checker` is owned by `cb` which is wrapped in `post_cb`. `post_cb` is
|
|
// deleted on a different thread which triggers a PostTask() back to the test
|
|
// main thread to destroy `cb` and `checker`.
|
|
RepeatingClosure cb =
|
|
BindRepeating(&SequenceRestrictionChecker::Run, std::move(checker));
|
|
RepeatingClosure post_cb = BindPostTask(task_runner_, std::move(cb));
|
|
target_thread.task_runner()->PostTask(
|
|
FROM_HERE, BindRepeating(&ClearReference, std::move(post_cb)));
|
|
|
|
target_thread.FlushForTesting();
|
|
EXPECT_FALSE(destroyed);
|
|
RunLoop().RunUntilIdle();
|
|
EXPECT_TRUE(destroyed);
|
|
}
|
|
|
|
} // namespace base
|