| // 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 |