1 // Copyright 2016 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/threading/post_task_and_reply_impl.h"
6
7 #include <utility>
8
9 #include "base/auto_reset.h"
10 #include "base/functional/bind.h"
11 #include "base/functional/callback_helpers.h"
12 #include "base/memory/raw_ptr.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/memory/weak_ptr.h"
15 #include "base/task/sequenced_task_runner.h"
16 #include "base/test/test_mock_time_task_runner.h"
17 #include "testing/gmock/include/gmock/gmock.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19
20 using ::testing::_;
21
22 namespace base::internal {
23
24 namespace {
25
26 class ObjectToDelete : public RefCounted<ObjectToDelete> {
27 public:
28 // `delete_flag` is set to true when this object is deleted
ObjectToDelete(bool * delete_flag)29 explicit ObjectToDelete(bool* delete_flag) : delete_flag_(delete_flag) {
30 EXPECT_FALSE(*delete_flag_);
31 }
32
33 ObjectToDelete(const ObjectToDelete&) = delete;
34 ObjectToDelete& operator=(const ObjectToDelete&) = delete;
35
36 private:
37 friend class RefCounted<ObjectToDelete>;
~ObjectToDelete()38 ~ObjectToDelete() { *delete_flag_ = true; }
39
40 const raw_ptr<bool> delete_flag_;
41 };
42
43 class MockObject {
44 public:
45 MockObject() = default;
46
47 MockObject(const MockObject&) = delete;
48 MockObject& operator=(const MockObject&) = delete;
49
50 MOCK_METHOD1(Task, void(scoped_refptr<ObjectToDelete>));
51 MOCK_METHOD1(Reply, void(scoped_refptr<ObjectToDelete>));
52
GetWeakPtr()53 WeakPtr<MockObject> GetWeakPtr() { return weak_factory_.GetWeakPtr(); }
54
InvalidateWeakPtrs()55 void InvalidateWeakPtrs() { weak_factory_.InvalidateWeakPtrs(); }
56
57 private:
58 WeakPtrFactory<MockObject> weak_factory_{this};
59 };
60
61 class MockRunsTasksInCurrentSequenceTaskRunner : public TestMockTimeTaskRunner {
62 public:
MockRunsTasksInCurrentSequenceTaskRunner(TestMockTimeTaskRunner::Type type=TestMockTimeTaskRunner::Type::kStandalone)63 MockRunsTasksInCurrentSequenceTaskRunner(
64 TestMockTimeTaskRunner::Type type =
65 TestMockTimeTaskRunner::Type::kStandalone)
66 : TestMockTimeTaskRunner(type) {}
67
68 MockRunsTasksInCurrentSequenceTaskRunner(
69 const MockRunsTasksInCurrentSequenceTaskRunner&) = delete;
70 MockRunsTasksInCurrentSequenceTaskRunner& operator=(
71 const MockRunsTasksInCurrentSequenceTaskRunner&) = delete;
72
StopAcceptingTasks()73 void StopAcceptingTasks() { accepts_tasks_ = false; }
74
RunUntilIdleWithRunsTasksInCurrentSequence()75 void RunUntilIdleWithRunsTasksInCurrentSequence() {
76 AutoReset<bool> reset(&runs_tasks_in_current_sequence_, true);
77 RunUntilIdle();
78 }
79
ClearPendingTasksWithRunsTasksInCurrentSequence()80 void ClearPendingTasksWithRunsTasksInCurrentSequence() {
81 AutoReset<bool> reset(&runs_tasks_in_current_sequence_, true);
82 ClearPendingTasks();
83 }
84
85 // TestMockTimeTaskRunner:
RunsTasksInCurrentSequence() const86 bool RunsTasksInCurrentSequence() const override {
87 return runs_tasks_in_current_sequence_;
88 }
89
PostDelayedTask(const Location & from_here,OnceClosure task,TimeDelta delay)90 bool PostDelayedTask(const Location& from_here,
91 OnceClosure task,
92 TimeDelta delay) override {
93 if (!accepts_tasks_)
94 return false;
95
96 return TestMockTimeTaskRunner::PostDelayedTask(from_here, std::move(task),
97 delay);
98 }
99
100 private:
101 ~MockRunsTasksInCurrentSequenceTaskRunner() override = default;
102
103 bool accepts_tasks_ = true;
104 bool runs_tasks_in_current_sequence_ = false;
105 };
106
107 class PostTaskAndReplyImplTest : public testing::Test {
108 public:
109 PostTaskAndReplyImplTest(const PostTaskAndReplyImplTest&) = delete;
110 PostTaskAndReplyImplTest& operator=(const PostTaskAndReplyImplTest&) = delete;
111
112 protected:
113 PostTaskAndReplyImplTest() = default;
114
PostTaskAndReplyToMockObject(bool task_uses_weak_ptr=false)115 bool PostTaskAndReplyToMockObject(bool task_uses_weak_ptr = false) {
116 OnceClosure task;
117 if (task_uses_weak_ptr) {
118 task = BindOnce(&MockObject::Task, mock_object_.GetWeakPtr(),
119 MakeRefCounted<ObjectToDelete>(&delete_task_flag_));
120 } else {
121 task = BindOnce(&MockObject::Task, Unretained(&mock_object_),
122 MakeRefCounted<ObjectToDelete>(&delete_task_flag_));
123 }
124
125 return PostTaskAndReplyImpl(
126 [this](const Location& location, OnceClosure task) {
127 return post_runner_->PostTask(location, std::move(task));
128 },
129 FROM_HERE, std::move(task),
130 BindOnce(&MockObject::Reply, Unretained(&mock_object_),
131 MakeRefCounted<ObjectToDelete>(&delete_reply_flag_)));
132 }
133
ExpectPostTaskAndReplyToMockObjectSucceeds(bool task_uses_weak_ptr=false)134 void ExpectPostTaskAndReplyToMockObjectSucceeds(
135 bool task_uses_weak_ptr = false) {
136 // Expect the post to succeed.
137 EXPECT_TRUE(PostTaskAndReplyToMockObject(task_uses_weak_ptr));
138
139 // Expect the first task to be posted to `post_runner_`.
140 EXPECT_TRUE(post_runner_->HasPendingTask());
141 EXPECT_FALSE(reply_runner_->HasPendingTask());
142 EXPECT_FALSE(delete_task_flag_);
143 EXPECT_FALSE(delete_reply_flag_);
144 }
145
146 scoped_refptr<MockRunsTasksInCurrentSequenceTaskRunner> post_runner_ =
147 MakeRefCounted<MockRunsTasksInCurrentSequenceTaskRunner>();
148 scoped_refptr<MockRunsTasksInCurrentSequenceTaskRunner> reply_runner_ =
149 MakeRefCounted<MockRunsTasksInCurrentSequenceTaskRunner>(
150 TestMockTimeTaskRunner::Type::kBoundToThread);
151 testing::StrictMock<MockObject> mock_object_;
152 bool delete_task_flag_ = false;
153 bool delete_reply_flag_ = false;
154 };
155
156 } // namespace
157
TEST_F(PostTaskAndReplyImplTest,PostTaskAndReply)158 TEST_F(PostTaskAndReplyImplTest, PostTaskAndReply) {
159 ExpectPostTaskAndReplyToMockObjectSucceeds();
160
161 EXPECT_CALL(mock_object_, Task(_));
162 post_runner_->RunUntilIdleWithRunsTasksInCurrentSequence();
163 testing::Mock::VerifyAndClear(&mock_object_);
164 // The task should have been deleted right after being run.
165 EXPECT_TRUE(delete_task_flag_);
166 EXPECT_FALSE(delete_reply_flag_);
167
168 // Expect the reply to be posted to `reply_runner_`.
169 EXPECT_FALSE(post_runner_->HasPendingTask());
170 EXPECT_TRUE(reply_runner_->HasPendingTask());
171
172 EXPECT_CALL(mock_object_, Reply(_));
173 reply_runner_->RunUntilIdleWithRunsTasksInCurrentSequence();
174 testing::Mock::VerifyAndClear(&mock_object_);
175 EXPECT_TRUE(delete_task_flag_);
176 // The reply should have been deleted right after being run.
177 EXPECT_TRUE(delete_reply_flag_);
178
179 // Expect no pending task in `post_runner_` and `reply_runner_`.
180 EXPECT_FALSE(post_runner_->HasPendingTask());
181 EXPECT_FALSE(reply_runner_->HasPendingTask());
182 }
183
TEST_F(PostTaskAndReplyImplTest,TaskDoesNotRun)184 TEST_F(PostTaskAndReplyImplTest, TaskDoesNotRun) {
185 ExpectPostTaskAndReplyToMockObjectSucceeds();
186
187 // Clear the `post_runner_`. Both callbacks should be scheduled for deletion
188 // on the `reply_runner_`.
189 post_runner_->ClearPendingTasksWithRunsTasksInCurrentSequence();
190 EXPECT_FALSE(post_runner_->HasPendingTask());
191 EXPECT_TRUE(reply_runner_->HasPendingTask());
192 EXPECT_FALSE(delete_task_flag_);
193 EXPECT_FALSE(delete_reply_flag_);
194
195 // Run the `reply_runner_`. Both callbacks should be deleted.
196 reply_runner_->RunUntilIdleWithRunsTasksInCurrentSequence();
197 EXPECT_TRUE(delete_task_flag_);
198 EXPECT_TRUE(delete_reply_flag_);
199 }
200
TEST_F(PostTaskAndReplyImplTest,ReplyDoesNotRun)201 TEST_F(PostTaskAndReplyImplTest, ReplyDoesNotRun) {
202 ExpectPostTaskAndReplyToMockObjectSucceeds();
203
204 EXPECT_CALL(mock_object_, Task(_));
205 post_runner_->RunUntilIdleWithRunsTasksInCurrentSequence();
206 testing::Mock::VerifyAndClear(&mock_object_);
207 // The task should have been deleted right after being run.
208 EXPECT_TRUE(delete_task_flag_);
209 EXPECT_FALSE(delete_reply_flag_);
210
211 // Expect the reply to be posted to `reply_runner_`.
212 EXPECT_FALSE(post_runner_->HasPendingTask());
213 EXPECT_TRUE(reply_runner_->HasPendingTask());
214
215 // Clear the `reply_runner_` queue without running tasks. The reply callback
216 // should be deleted.
217 reply_runner_->ClearPendingTasksWithRunsTasksInCurrentSequence();
218 EXPECT_TRUE(delete_task_flag_);
219 EXPECT_TRUE(delete_reply_flag_);
220 }
221
222 // This is a regression test for crbug.com/922938.
TEST_F(PostTaskAndReplyImplTest,PostTaskToStoppedTaskRunnerWithoutSequencedContext)223 TEST_F(PostTaskAndReplyImplTest,
224 PostTaskToStoppedTaskRunnerWithoutSequencedContext) {
225 reply_runner_.reset();
226 EXPECT_FALSE(SequencedTaskRunner::HasCurrentDefault());
227 post_runner_->StopAcceptingTasks();
228
229 // Expect the post to return false, but not to crash.
230 EXPECT_FALSE(PostTaskAndReplyToMockObject());
231
232 // Expect all tasks to be deleted.
233 EXPECT_TRUE(delete_task_flag_);
234 EXPECT_TRUE(delete_reply_flag_);
235 }
236
237 // Demonstrate that even if a task is not run because a weak pointer is
238 // invalidated, the reply still runs.
TEST_F(PostTaskAndReplyImplTest,ReplyStilRunsAfterInvalidatedWeakPtrTask)239 TEST_F(PostTaskAndReplyImplTest, ReplyStilRunsAfterInvalidatedWeakPtrTask) {
240 ExpectPostTaskAndReplyToMockObjectSucceeds(/*task_uses_weak_ptr=*/true);
241
242 // The task will not run when the provided weak pointer is invalidated.
243 EXPECT_CALL(mock_object_, Task(_)).Times(0);
244 mock_object_.InvalidateWeakPtrs();
245 post_runner_->RunUntilIdleWithRunsTasksInCurrentSequence();
246 testing::Mock::VerifyAndClear(&mock_object_);
247 // The task should have been deleted as part of dropping the run because of
248 // invalidated weak pointer.
249 EXPECT_TRUE(delete_task_flag_);
250 EXPECT_FALSE(delete_reply_flag_);
251
252 // Still expect a reply to be posted to `reply_runner_`.
253 EXPECT_FALSE(post_runner_->HasPendingTask());
254 EXPECT_TRUE(reply_runner_->HasPendingTask());
255
256 EXPECT_CALL(mock_object_, Reply(_)).Times(1);
257 reply_runner_->RunUntilIdleWithRunsTasksInCurrentSequence();
258 testing::Mock::VerifyAndClear(&mock_object_);
259 EXPECT_TRUE(delete_task_flag_);
260 // The reply should have been deleted right after being run.
261 EXPECT_TRUE(delete_reply_flag_);
262
263 // Expect no pending task in `post_runner_` and `reply_runner_`.
264 EXPECT_FALSE(post_runner_->HasPendingTask());
265 EXPECT_FALSE(reply_runner_->HasPendingTask());
266 }
267
268 } // namespace base::internal
269