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