Home | History | Annotate | Download | only in message_loop
      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:
     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 
    107 StaticAtomicSequenceNumber MessageLoopProxyTest::g_order;
    108 
    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 
    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 
    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 
    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