1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
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/message_loop/message_loop_task_runner.h"
6
7 #include <memory>
8
9 #include "base/atomic_sequence_num.h"
10 #include "base/bind.h"
11 #include "base/debug/leak_annotations.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/message_loop/message_loop_task_runner.h"
14 #include "base/run_loop.h"
15 #include "base/synchronization/waitable_event.h"
16 #include "base/threading/thread.h"
17 #include "base/threading/thread_task_runner_handle.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "testing/platform_test.h"
20
21 namespace base {
22
23 class MessageLoopTaskRunnerTest : public testing::Test {
24 public:
MessageLoopTaskRunnerTest()25 MessageLoopTaskRunnerTest()
26 : current_loop_(new MessageLoop()),
27 task_thread_("task_thread"),
28 thread_sync_(WaitableEvent::ResetPolicy::MANUAL,
29 WaitableEvent::InitialState::NOT_SIGNALED) {}
30
DeleteCurrentMessageLoop()31 void DeleteCurrentMessageLoop() { current_loop_.reset(); }
32
33 protected:
SetUp()34 void SetUp() override {
35 // Use SetUp() instead of the constructor to avoid posting a task to a
36 // partially constructed object.
37 task_thread_.Start();
38
39 // Allow us to pause the |task_thread_|'s MessageLoop.
40 task_thread_.task_runner()->PostTask(
41 FROM_HERE, BindOnce(&MessageLoopTaskRunnerTest::BlockTaskThreadHelper,
42 Unretained(this)));
43 }
44
TearDown()45 void TearDown() override {
46 // Make sure the |task_thread_| is not blocked, and stop the thread
47 // fully before destruction because its tasks may still depend on the
48 // |thread_sync_| event.
49 thread_sync_.Signal();
50 task_thread_.Stop();
51 DeleteCurrentMessageLoop();
52 }
53
54 // Make LoopRecorder threadsafe so that there is defined behavior even if a
55 // threading mistake sneaks into the PostTaskAndReplyRelay implementation.
56 class LoopRecorder : public RefCountedThreadSafe<LoopRecorder> {
57 public:
LoopRecorder(MessageLoop ** run_on,MessageLoop ** deleted_on,int * destruct_order)58 LoopRecorder(MessageLoop** run_on,
59 MessageLoop** deleted_on,
60 int* destruct_order)
61 : run_on_(run_on),
62 deleted_on_(deleted_on),
63 destruct_order_(destruct_order) {}
64
RecordRun()65 void RecordRun() { *run_on_ = MessageLoop::current(); }
66
67 private:
68 friend class RefCountedThreadSafe<LoopRecorder>;
~LoopRecorder()69 ~LoopRecorder() {
70 *deleted_on_ = MessageLoop::current();
71 *destruct_order_ = g_order.GetNext();
72 }
73
74 MessageLoop** run_on_;
75 MessageLoop** deleted_on_;
76 int* destruct_order_;
77 };
78
RecordLoop(scoped_refptr<LoopRecorder> recorder)79 static void RecordLoop(scoped_refptr<LoopRecorder> recorder) {
80 recorder->RecordRun();
81 }
82
RecordLoopAndQuit(scoped_refptr<LoopRecorder> recorder)83 static void RecordLoopAndQuit(scoped_refptr<LoopRecorder> recorder) {
84 recorder->RecordRun();
85 RunLoop::QuitCurrentWhenIdleDeprecated();
86 }
87
UnblockTaskThread()88 void UnblockTaskThread() { thread_sync_.Signal(); }
89
BlockTaskThreadHelper()90 void BlockTaskThreadHelper() { thread_sync_.Wait(); }
91
92 static AtomicSequenceNumber g_order;
93
94 std::unique_ptr<MessageLoop> current_loop_;
95 Thread task_thread_;
96
97 private:
98 base::WaitableEvent thread_sync_;
99 };
100
101 AtomicSequenceNumber MessageLoopTaskRunnerTest::g_order;
102
TEST_F(MessageLoopTaskRunnerTest,PostTaskAndReply_Basic)103 TEST_F(MessageLoopTaskRunnerTest, PostTaskAndReply_Basic) {
104 MessageLoop* task_run_on = nullptr;
105 MessageLoop* task_deleted_on = nullptr;
106 int task_delete_order = -1;
107 MessageLoop* reply_run_on = nullptr;
108 MessageLoop* reply_deleted_on = nullptr;
109 int reply_delete_order = -1;
110
111 scoped_refptr<LoopRecorder> task_recorder =
112 new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order);
113 scoped_refptr<LoopRecorder> reply_recorder =
114 new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order);
115
116 ASSERT_TRUE(task_thread_.task_runner()->PostTaskAndReply(
117 FROM_HERE, BindOnce(&RecordLoop, task_recorder),
118 BindOnce(&RecordLoopAndQuit, reply_recorder)));
119
120 // Die if base::Bind doesn't retain a reference to the recorders.
121 task_recorder = nullptr;
122 reply_recorder = nullptr;
123 ASSERT_FALSE(task_deleted_on);
124 ASSERT_FALSE(reply_deleted_on);
125
126 UnblockTaskThread();
127 RunLoop().Run();
128
129 EXPECT_EQ(task_thread_.message_loop(), task_run_on);
130 EXPECT_EQ(task_thread_.message_loop(), task_deleted_on);
131 EXPECT_EQ(current_loop_.get(), reply_run_on);
132 EXPECT_EQ(current_loop_.get(), reply_deleted_on);
133 EXPECT_LT(task_delete_order, reply_delete_order);
134 }
135
TEST_F(MessageLoopTaskRunnerTest,PostTaskAndReplyOnDeletedThreadDoesNotLeak)136 TEST_F(MessageLoopTaskRunnerTest, PostTaskAndReplyOnDeletedThreadDoesNotLeak) {
137 MessageLoop* task_run_on = nullptr;
138 MessageLoop* task_deleted_on = nullptr;
139 int task_delete_order = -1;
140 MessageLoop* reply_run_on = nullptr;
141 MessageLoop* reply_deleted_on = nullptr;
142 int reply_delete_order = -1;
143
144 scoped_refptr<LoopRecorder> task_recorder =
145 new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order);
146 scoped_refptr<LoopRecorder> reply_recorder =
147 new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order);
148
149 // Grab a task runner to a dead MessageLoop.
150 scoped_refptr<SingleThreadTaskRunner> task_runner =
151 task_thread_.task_runner();
152 UnblockTaskThread();
153 task_thread_.Stop();
154
155 ASSERT_FALSE(task_runner->PostTaskAndReply(
156 FROM_HERE, BindOnce(&RecordLoop, task_recorder),
157 BindOnce(&RecordLoopAndQuit, reply_recorder)));
158
159 // The relay should have properly deleted its resources leaving us as the only
160 // reference.
161 EXPECT_EQ(task_delete_order, reply_delete_order);
162 ASSERT_TRUE(task_recorder->HasOneRef());
163 ASSERT_TRUE(reply_recorder->HasOneRef());
164
165 // Nothing should have run though.
166 EXPECT_FALSE(task_run_on);
167 EXPECT_FALSE(reply_run_on);
168 }
169
TEST_F(MessageLoopTaskRunnerTest,PostTaskAndReply_SameLoop)170 TEST_F(MessageLoopTaskRunnerTest, PostTaskAndReply_SameLoop) {
171 MessageLoop* task_run_on = nullptr;
172 MessageLoop* task_deleted_on = nullptr;
173 int task_delete_order = -1;
174 MessageLoop* reply_run_on = nullptr;
175 MessageLoop* reply_deleted_on = nullptr;
176 int reply_delete_order = -1;
177
178 scoped_refptr<LoopRecorder> task_recorder =
179 new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order);
180 scoped_refptr<LoopRecorder> reply_recorder =
181 new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order);
182
183 // Enqueue the relay.
184 ASSERT_TRUE(current_loop_->task_runner()->PostTaskAndReply(
185 FROM_HERE, BindOnce(&RecordLoop, task_recorder),
186 BindOnce(&RecordLoopAndQuit, reply_recorder)));
187
188 // Die if base::Bind doesn't retain a reference to the recorders.
189 task_recorder = nullptr;
190 reply_recorder = nullptr;
191 ASSERT_FALSE(task_deleted_on);
192 ASSERT_FALSE(reply_deleted_on);
193
194 RunLoop().Run();
195
196 EXPECT_EQ(current_loop_.get(), task_run_on);
197 EXPECT_EQ(current_loop_.get(), task_deleted_on);
198 EXPECT_EQ(current_loop_.get(), reply_run_on);
199 EXPECT_EQ(current_loop_.get(), reply_deleted_on);
200 EXPECT_LT(task_delete_order, reply_delete_order);
201 }
202
TEST_F(MessageLoopTaskRunnerTest,PostTaskAndReply_DeadReplyTaskRunnerBehavior)203 TEST_F(MessageLoopTaskRunnerTest,
204 PostTaskAndReply_DeadReplyTaskRunnerBehavior) {
205 // Annotate the scope as having memory leaks to suppress heapchecker reports.
206 ANNOTATE_SCOPED_MEMORY_LEAK;
207 MessageLoop* task_run_on = nullptr;
208 MessageLoop* task_deleted_on = nullptr;
209 int task_delete_order = -1;
210 MessageLoop* reply_run_on = nullptr;
211 MessageLoop* reply_deleted_on = nullptr;
212 int reply_delete_order = -1;
213
214 scoped_refptr<LoopRecorder> task_recorder =
215 new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order);
216 scoped_refptr<LoopRecorder> reply_recorder =
217 new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order);
218
219 // Enqueue the relay.
220 task_thread_.task_runner()->PostTaskAndReply(
221 FROM_HERE, BindOnce(&RecordLoop, task_recorder),
222 BindOnce(&RecordLoopAndQuit, reply_recorder));
223
224 // Die if base::Bind doesn't retain a reference to the recorders.
225 task_recorder = nullptr;
226 reply_recorder = nullptr;
227 ASSERT_FALSE(task_deleted_on);
228 ASSERT_FALSE(reply_deleted_on);
229
230 UnblockTaskThread();
231
232 // Mercilessly whack the current loop before |reply| gets to run.
233 current_loop_.reset();
234
235 // This should ensure the relay has been run. We need to record the
236 // MessageLoop pointer before stopping the thread because Thread::Stop() will
237 // NULL out its own pointer.
238 MessageLoop* task_loop = task_thread_.message_loop();
239 task_thread_.Stop();
240
241 // Even if the reply task runner is already gone, the original task should
242 // already be deleted. However, the reply which hasn't executed yet should
243 // leak to avoid thread-safety issues.
244 EXPECT_EQ(task_loop, task_run_on);
245 EXPECT_EQ(task_loop, task_deleted_on);
246 EXPECT_FALSE(reply_run_on);
247 ASSERT_FALSE(reply_deleted_on);
248
249 // The PostTaskAndReplyRelay is leaked here. Even if we had a reference to
250 // it, we cannot just delete it because PostTaskAndReplyRelay's destructor
251 // checks that MessageLoop::current() is the the same as when the
252 // PostTaskAndReplyRelay object was constructed. However, this loop must have
253 // already been deleted in order to perform this test. See
254 // http://crbug.com/86301.
255 }
256
257 class MessageLoopTaskRunnerThreadingTest : public testing::Test {
258 public:
Release() const259 void Release() const {
260 AssertOnIOThread();
261 Quit();
262 }
263
Quit() const264 void Quit() const {
265 loop_.task_runner()->PostTask(
266 FROM_HERE, RunLoop::QuitCurrentWhenIdleClosureDeprecated());
267 }
268
AssertOnIOThread() const269 void AssertOnIOThread() const {
270 ASSERT_TRUE(io_thread_->task_runner()->BelongsToCurrentThread());
271 ASSERT_EQ(io_thread_->task_runner(), ThreadTaskRunnerHandle::Get());
272 }
273
AssertOnFileThread() const274 void AssertOnFileThread() const {
275 ASSERT_TRUE(file_thread_->task_runner()->BelongsToCurrentThread());
276 ASSERT_EQ(file_thread_->task_runner(), ThreadTaskRunnerHandle::Get());
277 }
278
279 protected:
SetUp()280 void SetUp() override {
281 io_thread_.reset(new Thread("MessageLoopTaskRunnerThreadingTest_IO"));
282 file_thread_.reset(new Thread("MessageLoopTaskRunnerThreadingTest_File"));
283 io_thread_->Start();
284 file_thread_->Start();
285 }
286
TearDown()287 void TearDown() override {
288 io_thread_->Stop();
289 file_thread_->Stop();
290 }
291
BasicFunction(MessageLoopTaskRunnerThreadingTest * test)292 static void BasicFunction(MessageLoopTaskRunnerThreadingTest* test) {
293 test->AssertOnFileThread();
294 test->Quit();
295 }
296
AssertNotRun()297 static void AssertNotRun() { FAIL() << "Callback Should not get executed."; }
298
299 class DeletedOnFile {
300 public:
DeletedOnFile(MessageLoopTaskRunnerThreadingTest * test)301 explicit DeletedOnFile(MessageLoopTaskRunnerThreadingTest* test)
302 : test_(test) {}
303
~DeletedOnFile()304 ~DeletedOnFile() {
305 test_->AssertOnFileThread();
306 test_->Quit();
307 }
308
309 private:
310 MessageLoopTaskRunnerThreadingTest* test_;
311 };
312
313 std::unique_ptr<Thread> io_thread_;
314 std::unique_ptr<Thread> file_thread_;
315
316 private:
317 mutable MessageLoop loop_;
318 };
319
TEST_F(MessageLoopTaskRunnerThreadingTest,Release)320 TEST_F(MessageLoopTaskRunnerThreadingTest, Release) {
321 EXPECT_TRUE(io_thread_->task_runner()->ReleaseSoon(FROM_HERE, this));
322 RunLoop().Run();
323 }
324
TEST_F(MessageLoopTaskRunnerThreadingTest,Delete)325 TEST_F(MessageLoopTaskRunnerThreadingTest, Delete) {
326 DeletedOnFile* deleted_on_file = new DeletedOnFile(this);
327 EXPECT_TRUE(
328 file_thread_->task_runner()->DeleteSoon(FROM_HERE, deleted_on_file));
329 RunLoop().Run();
330 }
331
TEST_F(MessageLoopTaskRunnerThreadingTest,PostTask)332 TEST_F(MessageLoopTaskRunnerThreadingTest, PostTask) {
333 EXPECT_TRUE(file_thread_->task_runner()->PostTask(
334 FROM_HERE, BindOnce(&MessageLoopTaskRunnerThreadingTest::BasicFunction,
335 Unretained(this))));
336 RunLoop().Run();
337 }
338
TEST_F(MessageLoopTaskRunnerThreadingTest,PostTaskAfterThreadExits)339 TEST_F(MessageLoopTaskRunnerThreadingTest, PostTaskAfterThreadExits) {
340 std::unique_ptr<Thread> test_thread(
341 new Thread("MessageLoopTaskRunnerThreadingTest_Dummy"));
342 test_thread->Start();
343 scoped_refptr<SingleThreadTaskRunner> task_runner =
344 test_thread->task_runner();
345 test_thread->Stop();
346
347 bool ret = task_runner->PostTask(
348 FROM_HERE, BindOnce(&MessageLoopTaskRunnerThreadingTest::AssertNotRun));
349 EXPECT_FALSE(ret);
350 }
351
TEST_F(MessageLoopTaskRunnerThreadingTest,PostTaskAfterThreadIsDeleted)352 TEST_F(MessageLoopTaskRunnerThreadingTest, PostTaskAfterThreadIsDeleted) {
353 scoped_refptr<SingleThreadTaskRunner> task_runner;
354 {
355 std::unique_ptr<Thread> test_thread(
356 new Thread("MessageLoopTaskRunnerThreadingTest_Dummy"));
357 test_thread->Start();
358 task_runner = test_thread->task_runner();
359 }
360 bool ret = task_runner->PostTask(
361 FROM_HERE, BindOnce(&MessageLoopTaskRunnerThreadingTest::AssertNotRun));
362 EXPECT_FALSE(ret);
363 }
364
365 } // namespace base
366