• 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_proxy.h"
6 
7 #include "base/atomic_sequence_num.h"
8 #include "base/bind.h"
9 #include "base/debug/leak_annotations.h"
10 #include "base/memory/ref_counted.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/synchronization/waitable_event.h"
14 #include "base/threading/thread.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 
17 namespace base {
18 
19 namespace {
20 
21 class MessageLoopProxyTest : public testing::Test {
22  public:
MessageLoopProxyTest()23   MessageLoopProxyTest()
24       : current_loop_(new MessageLoop()),
25         task_thread_("task_thread"),
26         thread_sync_(true, false) {
27   }
28 
DeleteCurrentMessageLoop()29   void DeleteCurrentMessageLoop() {
30     current_loop_.reset();
31   }
32 
33  protected:
SetUp()34   virtual void SetUp() OVERRIDE {
35     // Use SetUp() instead of the constructor to avoid posting a task to a
36     // partialy constructed object.
37     task_thread_.Start();
38 
39     // Allow us to pause the |task_thread_|'s MessageLoop.
40     task_thread_.message_loop()->PostTask(
41         FROM_HERE,
42         Bind(&MessageLoopProxyTest::BlockTaskThreadHelper, Unretained(this)));
43   }
44 
TearDown()45   virtual void TearDown() OVERRIDE {
46     // Make sure the |task_thread_| is not blocked, and stop the thread
47     // fully before destuction 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, MessageLoop** deleted_on,
59                  int* destruct_order)
60         : run_on_(run_on),
61           deleted_on_(deleted_on),
62           destruct_order_(destruct_order) {
63     }
64 
RecordRun()65     void RecordRun() {
66       *run_on_ = MessageLoop::current();
67     }
68 
69    private:
70     friend class RefCountedThreadSafe<LoopRecorder>;
~LoopRecorder()71     ~LoopRecorder() {
72       *deleted_on_ = MessageLoop::current();
73       *destruct_order_ = g_order.GetNext();
74     }
75 
76     MessageLoop** run_on_;
77     MessageLoop** deleted_on_;
78     int* destruct_order_;
79   };
80 
RecordLoop(scoped_refptr<LoopRecorder> recorder)81   static void RecordLoop(scoped_refptr<LoopRecorder> recorder) {
82     recorder->RecordRun();
83   }
84 
RecordLoopAndQuit(scoped_refptr<LoopRecorder> recorder)85   static void RecordLoopAndQuit(scoped_refptr<LoopRecorder> recorder) {
86     recorder->RecordRun();
87     MessageLoop::current()->QuitWhenIdle();
88   }
89 
UnblockTaskThread()90   void UnblockTaskThread() {
91     thread_sync_.Signal();
92   }
93 
BlockTaskThreadHelper()94   void BlockTaskThreadHelper() {
95     thread_sync_.Wait();
96   }
97 
98   static StaticAtomicSequenceNumber g_order;
99 
100   scoped_ptr<MessageLoop> current_loop_;
101   Thread task_thread_;
102 
103  private:
104   base::WaitableEvent thread_sync_;
105 };
106 
107 StaticAtomicSequenceNumber MessageLoopProxyTest::g_order;
108 
TEST_F(MessageLoopProxyTest,PostTaskAndReply_Basic)109 TEST_F(MessageLoopProxyTest, PostTaskAndReply_Basic) {
110   MessageLoop* task_run_on = NULL;
111   MessageLoop* task_deleted_on = NULL;
112   int task_delete_order = -1;
113   MessageLoop* reply_run_on = NULL;
114   MessageLoop* reply_deleted_on = NULL;
115   int reply_delete_order = -1;
116 
117   scoped_refptr<LoopRecorder> task_recoder =
118       new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order);
119   scoped_refptr<LoopRecorder> reply_recoder =
120       new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order);
121 
122   ASSERT_TRUE(task_thread_.message_loop_proxy()->PostTaskAndReply(
123       FROM_HERE,
124       Bind(&RecordLoop, task_recoder),
125       Bind(&RecordLoopAndQuit, reply_recoder)));
126 
127   // Die if base::Bind doesn't retain a reference to the recorders.
128   task_recoder = NULL;
129   reply_recoder = NULL;
130   ASSERT_FALSE(task_deleted_on);
131   ASSERT_FALSE(reply_deleted_on);
132 
133   UnblockTaskThread();
134   current_loop_->Run();
135 
136   EXPECT_EQ(task_thread_.message_loop(), task_run_on);
137   EXPECT_EQ(current_loop_.get(), task_deleted_on);
138   EXPECT_EQ(current_loop_.get(), reply_run_on);
139   EXPECT_EQ(current_loop_.get(), reply_deleted_on);
140   EXPECT_LT(task_delete_order, reply_delete_order);
141 }
142 
TEST_F(MessageLoopProxyTest,PostTaskAndReplyOnDeletedThreadDoesNotLeak)143 TEST_F(MessageLoopProxyTest, PostTaskAndReplyOnDeletedThreadDoesNotLeak) {
144   MessageLoop* task_run_on = NULL;
145   MessageLoop* task_deleted_on = NULL;
146   int task_delete_order = -1;
147   MessageLoop* reply_run_on = NULL;
148   MessageLoop* reply_deleted_on = NULL;
149   int reply_delete_order = -1;
150 
151   scoped_refptr<LoopRecorder> task_recoder =
152       new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order);
153   scoped_refptr<LoopRecorder> reply_recoder =
154       new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order);
155 
156   // Grab a MessageLoopProxy to a dead MessageLoop.
157   scoped_refptr<MessageLoopProxy> task_loop_proxy =
158       task_thread_.message_loop_proxy();
159   UnblockTaskThread();
160   task_thread_.Stop();
161 
162   ASSERT_FALSE(task_loop_proxy->PostTaskAndReply(
163       FROM_HERE,
164       Bind(&RecordLoop, task_recoder),
165       Bind(&RecordLoopAndQuit, reply_recoder)));
166 
167   // The relay should have properly deleted its resources leaving us as the only
168   // reference.
169   EXPECT_EQ(task_delete_order, reply_delete_order);
170   ASSERT_TRUE(task_recoder->HasOneRef());
171   ASSERT_TRUE(reply_recoder->HasOneRef());
172 
173   // Nothing should have run though.
174   EXPECT_FALSE(task_run_on);
175   EXPECT_FALSE(reply_run_on);
176 }
177 
TEST_F(MessageLoopProxyTest,PostTaskAndReply_SameLoop)178 TEST_F(MessageLoopProxyTest, PostTaskAndReply_SameLoop) {
179   MessageLoop* task_run_on = NULL;
180   MessageLoop* task_deleted_on = NULL;
181   int task_delete_order = -1;
182   MessageLoop* reply_run_on = NULL;
183   MessageLoop* reply_deleted_on = NULL;
184   int reply_delete_order = -1;
185 
186   scoped_refptr<LoopRecorder> task_recoder =
187       new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order);
188   scoped_refptr<LoopRecorder> reply_recoder =
189       new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order);
190 
191   // Enqueue the relay.
192   ASSERT_TRUE(current_loop_->message_loop_proxy()->PostTaskAndReply(
193       FROM_HERE,
194       Bind(&RecordLoop, task_recoder),
195       Bind(&RecordLoopAndQuit, reply_recoder)));
196 
197   // Die if base::Bind doesn't retain a reference to the recorders.
198   task_recoder = NULL;
199   reply_recoder = NULL;
200   ASSERT_FALSE(task_deleted_on);
201   ASSERT_FALSE(reply_deleted_on);
202 
203   current_loop_->Run();
204 
205   EXPECT_EQ(current_loop_.get(), task_run_on);
206   EXPECT_EQ(current_loop_.get(), task_deleted_on);
207   EXPECT_EQ(current_loop_.get(), reply_run_on);
208   EXPECT_EQ(current_loop_.get(), reply_deleted_on);
209   EXPECT_LT(task_delete_order, reply_delete_order);
210 }
211 
TEST_F(MessageLoopProxyTest,PostTaskAndReply_DeadReplyLoopDoesNotDelete)212 TEST_F(MessageLoopProxyTest, PostTaskAndReply_DeadReplyLoopDoesNotDelete) {
213   // Annotate the scope as having memory leaks to suppress heapchecker reports.
214   ANNOTATE_SCOPED_MEMORY_LEAK;
215   MessageLoop* task_run_on = NULL;
216   MessageLoop* task_deleted_on = NULL;
217   int task_delete_order = -1;
218   MessageLoop* reply_run_on = NULL;
219   MessageLoop* reply_deleted_on = NULL;
220   int reply_delete_order = -1;
221 
222   scoped_refptr<LoopRecorder> task_recoder =
223       new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order);
224   scoped_refptr<LoopRecorder> reply_recoder =
225       new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order);
226 
227   // Enqueue the relay.
228   task_thread_.message_loop_proxy()->PostTaskAndReply(
229       FROM_HERE,
230       Bind(&RecordLoop, task_recoder),
231       Bind(&RecordLoopAndQuit, reply_recoder));
232 
233   // Die if base::Bind doesn't retain a reference to the recorders.
234   task_recoder = NULL;
235   reply_recoder = NULL;
236   ASSERT_FALSE(task_deleted_on);
237   ASSERT_FALSE(reply_deleted_on);
238 
239   UnblockTaskThread();
240 
241   // Mercilessly whack the current loop before |reply| gets to run.
242   current_loop_.reset();
243 
244   // This should ensure the relay has been run.  We need to record the
245   // MessageLoop pointer before stopping the thread because Thread::Stop() will
246   // NULL out its own pointer.
247   MessageLoop* task_loop = task_thread_.message_loop();
248   task_thread_.Stop();
249 
250   EXPECT_EQ(task_loop, task_run_on);
251   ASSERT_FALSE(task_deleted_on);
252   EXPECT_FALSE(reply_run_on);
253   ASSERT_FALSE(reply_deleted_on);
254   EXPECT_EQ(task_delete_order, reply_delete_order);
255 
256   // The PostTaskAndReplyRelay is leaked here.  Even if we had a reference to
257   // it, we cannot just delete it because PostTaskAndReplyRelay's destructor
258   // checks that MessageLoop::current() is the the same as when the
259   // PostTaskAndReplyRelay object was constructed.  However, this loop must have
260   // aleady been deleted in order to perform this test.  See
261   // http://crbug.com/86301.
262 }
263 
264 }  // namespace
265 
266 }  // namespace base
267