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.
5 #include "base/message_loop_proxy.h"
7 #include "base/atomic_sequence_num.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.h"
13 #include "base/synchronization/waitable_event.h"
14 #include "base/threading/thread.h"
15 #include "testing/gtest/include/gtest/gtest.h"
21 class MessageLoopProxyTest
: public testing::Test
{
23 MessageLoopProxyTest()
24 : current_loop_(new MessageLoop()),
25 task_thread_("task_thread"),
26 thread_sync_(true, false) {
29 void DeleteCurrentMessageLoop() {
30 current_loop_
.reset();
34 virtual void SetUp() OVERRIDE
{
35 // Use SetUp() instead of the constructor to avoid posting a task to a
36 // partialy constructed object.
39 // Allow us to pause the |task_thread_|'s MessageLoop.
40 task_thread_
.message_loop()->PostTask(
42 Bind(&MessageLoopProxyTest::BlockTaskThreadHelper
, Unretained(this)));
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();
51 DeleteCurrentMessageLoop();
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
> {
58 LoopRecorder(MessageLoop
** run_on
, MessageLoop
** deleted_on
,
61 deleted_on_(deleted_on
),
62 destruct_order_(destruct_order
) {
66 *run_on_
= MessageLoop::current();
70 friend class RefCountedThreadSafe
<LoopRecorder
>;
72 *deleted_on_
= MessageLoop::current();
73 *destruct_order_
= g_order
.GetNext();
76 MessageLoop
** run_on_
;
77 MessageLoop
** deleted_on_
;
81 static void RecordLoop(scoped_refptr
<LoopRecorder
> recorder
) {
82 recorder
->RecordRun();
85 static void RecordLoopAndQuit(scoped_refptr
<LoopRecorder
> recorder
) {
86 recorder
->RecordRun();
87 MessageLoop::current()->Quit();
90 void UnblockTaskThread() {
91 thread_sync_
.Signal();
94 void BlockTaskThreadHelper() {
98 static StaticAtomicSequenceNumber g_order
;
100 scoped_ptr
<MessageLoop
> current_loop_
;
104 base::WaitableEvent thread_sync_
;
107 StaticAtomicSequenceNumber
MessageLoopProxyTest::g_order
;
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;
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
);
122 ASSERT_TRUE(task_thread_
.message_loop_proxy()->PostTaskAndReply(
124 Bind(&RecordLoop
, task_recoder
),
125 Bind(&RecordLoopAndQuit
, reply_recoder
)));
127 // Die if base::Bind doesn't retain a reference to the recorders.
129 reply_recoder
= NULL
;
130 ASSERT_FALSE(task_deleted_on
);
131 ASSERT_FALSE(reply_deleted_on
);
134 current_loop_
->Run();
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
);
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;
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
);
156 // Grab a MessageLoopProxy to a dead MessageLoop.
157 scoped_refptr
<MessageLoopProxy
> task_loop_proxy
=
158 task_thread_
.message_loop_proxy();
162 ASSERT_FALSE(task_loop_proxy
->PostTaskAndReply(
164 Bind(&RecordLoop
, task_recoder
),
165 Bind(&RecordLoopAndQuit
, reply_recoder
)));
167 // The relay should have properly deleted its resources leaving us as the only
169 EXPECT_EQ(task_delete_order
, reply_delete_order
);
170 ASSERT_TRUE(task_recoder
->HasOneRef());
171 ASSERT_TRUE(reply_recoder
->HasOneRef());
173 // Nothing should have run though.
174 EXPECT_FALSE(task_run_on
);
175 EXPECT_FALSE(reply_run_on
);
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;
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
);
191 // Enqueue the relay.
192 ASSERT_TRUE(current_loop_
->message_loop_proxy()->PostTaskAndReply(
194 Bind(&RecordLoop
, task_recoder
),
195 Bind(&RecordLoopAndQuit
, reply_recoder
)));
197 // Die if base::Bind doesn't retain a reference to the recorders.
199 reply_recoder
= NULL
;
200 ASSERT_FALSE(task_deleted_on
);
201 ASSERT_FALSE(reply_deleted_on
);
203 current_loop_
->Run();
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
);
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;
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
);
227 // Enqueue the relay.
228 task_thread_
.message_loop_proxy()->PostTaskAndReply(
230 Bind(&RecordLoop
, task_recoder
),
231 Bind(&RecordLoopAndQuit
, reply_recoder
));
233 // Die if base::Bind doesn't retain a reference to the recorders.
235 reply_recoder
= NULL
;
236 ASSERT_FALSE(task_deleted_on
);
237 ASSERT_FALSE(reply_deleted_on
);
241 // Mercilessly whack the current loop before |reply| gets to run.
242 current_loop_
.reset();
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();
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
);
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.