• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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