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