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