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
17namespace base {
18
19namespace {
20
21class MessageLoopProxyTest : public testing::Test {
22 public:
23  MessageLoopProxyTest()
24      : current_loop_(new MessageLoop()),
25        task_thread_("task_thread"),
26        thread_sync_(true, false) {
27  }
28
29  void DeleteCurrentMessageLoop() {
30    current_loop_.reset();
31  }
32
33 protected:
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
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:
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
65    void RecordRun() {
66      *run_on_ = MessageLoop::current();
67    }
68
69   private:
70    friend class RefCountedThreadSafe<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
81  static void RecordLoop(scoped_refptr<LoopRecorder> recorder) {
82    recorder->RecordRun();
83  }
84
85  static void RecordLoopAndQuit(scoped_refptr<LoopRecorder> recorder) {
86    recorder->RecordRun();
87    MessageLoop::current()->QuitWhenIdle();
88  }
89
90  void UnblockTaskThread() {
91    thread_sync_.Signal();
92  }
93
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
107StaticAtomicSequenceNumber MessageLoopProxyTest::g_order;
108
109TEST_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
143TEST_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
178TEST_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
212TEST_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