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_pump_glib.h"
      6 
      7 #include <glib.h>
      8 #include <math.h>
      9 
     10 #include <algorithm>
     11 #include <vector>
     12 
     13 #include "base/bind.h"
     14 #include "base/bind_helpers.h"
     15 #include "base/callback.h"
     16 #include "base/memory/ref_counted.h"
     17 #include "base/message_loop/message_loop.h"
     18 #include "base/run_loop.h"
     19 #include "base/threading/thread.h"
     20 #include "testing/gtest/include/gtest/gtest.h"
     21 
     22 #if defined(TOOLKIT_GTK)
     23 #include <gtk/gtk.h>
     24 #endif
     25 
     26 namespace base {
     27 namespace {
     28 
     29 // This class injects dummy "events" into the GLib loop. When "handled" these
     30 // events can run tasks. This is intended to mock gtk events (the corresponding
     31 // GLib source runs at the same priority).
     32 class EventInjector {
     33  public:
     34   EventInjector() : processed_events_(0) {
     35     source_ = static_cast<Source*>(g_source_new(&SourceFuncs, sizeof(Source)));
     36     source_->injector = this;
     37     g_source_attach(source_, NULL);
     38     g_source_set_can_recurse(source_, TRUE);
     39   }
     40 
     41   ~EventInjector() {
     42     g_source_destroy(source_);
     43     g_source_unref(source_);
     44   }
     45 
     46   int HandlePrepare() {
     47     // If the queue is empty, block.
     48     if (events_.empty())
     49       return -1;
     50     TimeDelta delta = events_[0].time - Time::NowFromSystemTime();
     51     return std::max(0, static_cast<int>(ceil(delta.InMillisecondsF())));
     52   }
     53 
     54   bool HandleCheck() {
     55     if (events_.empty())
     56       return false;
     57     return events_[0].time <= Time::NowFromSystemTime();
     58   }
     59 
     60   void HandleDispatch() {
     61     if (events_.empty())
     62       return;
     63     Event event = events_[0];
     64     events_.erase(events_.begin());
     65     ++processed_events_;
     66     if (!event.callback.is_null())
     67       event.callback.Run();
     68     else if (!event.task.is_null())
     69       event.task.Run();
     70   }
     71 
     72   // Adds an event to the queue. When "handled", executes |callback|.
     73   // delay_ms is relative to the last event if any, or to Now() otherwise.
     74   void AddEvent(int delay_ms, const Closure& callback) {
     75     AddEventHelper(delay_ms, callback, Closure());
     76   }
     77 
     78   void AddDummyEvent(int delay_ms) {
     79     AddEventHelper(delay_ms, Closure(), Closure());
     80   }
     81 
     82   void AddEventAsTask(int delay_ms, const Closure& task) {
     83     AddEventHelper(delay_ms, Closure(), task);
     84   }
     85 
     86   void Reset() {
     87     processed_events_ = 0;
     88     events_.clear();
     89   }
     90 
     91   int processed_events() const { return processed_events_; }
     92 
     93  private:
     94   struct Event {
     95     Time time;
     96     Closure callback;
     97     Closure task;
     98   };
     99 
    100   struct Source : public GSource {
    101     EventInjector* injector;
    102   };
    103 
    104   void AddEventHelper(
    105       int delay_ms, const Closure& callback, const Closure& task) {
    106     Time last_time;
    107     if (!events_.empty())
    108       last_time = (events_.end()-1)->time;
    109     else
    110       last_time = Time::NowFromSystemTime();
    111 
    112     Time future = last_time + TimeDelta::FromMilliseconds(delay_ms);
    113     EventInjector::Event event = {future, callback, task};
    114     events_.push_back(event);
    115   }
    116 
    117   static gboolean Prepare(GSource* source, gint* timeout_ms) {
    118     *timeout_ms = static_cast<Source*>(source)->injector->HandlePrepare();
    119     return FALSE;
    120   }
    121 
    122   static gboolean Check(GSource* source) {
    123     return static_cast<Source*>(source)->injector->HandleCheck();
    124   }
    125 
    126   static gboolean Dispatch(GSource* source,
    127                            GSourceFunc unused_func,
    128                            gpointer unused_data) {
    129     static_cast<Source*>(source)->injector->HandleDispatch();
    130     return TRUE;
    131   }
    132 
    133   Source* source_;
    134   std::vector<Event> events_;
    135   int processed_events_;
    136   static GSourceFuncs SourceFuncs;
    137   DISALLOW_COPY_AND_ASSIGN(EventInjector);
    138 };
    139 
    140 GSourceFuncs EventInjector::SourceFuncs = {
    141   EventInjector::Prepare,
    142   EventInjector::Check,
    143   EventInjector::Dispatch,
    144   NULL
    145 };
    146 
    147 void IncrementInt(int *value) {
    148   ++*value;
    149 }
    150 
    151 // Checks how many events have been processed by the injector.
    152 void ExpectProcessedEvents(EventInjector* injector, int count) {
    153   EXPECT_EQ(injector->processed_events(), count);
    154 }
    155 
    156 // Posts a task on the current message loop.
    157 void PostMessageLoopTask(const tracked_objects::Location& from_here,
    158                          const Closure& task) {
    159   MessageLoop::current()->PostTask(from_here, task);
    160 }
    161 
    162 // Test fixture.
    163 class MessagePumpGLibTest : public testing::Test {
    164  public:
    165   MessagePumpGLibTest() : loop_(NULL), injector_(NULL) { }
    166 
    167   // Overridden from testing::Test:
    168   virtual void SetUp() OVERRIDE {
    169     loop_ = new MessageLoop(MessageLoop::TYPE_UI);
    170     injector_ = new EventInjector();
    171   }
    172   virtual void TearDown() OVERRIDE {
    173     delete injector_;
    174     injector_ = NULL;
    175     delete loop_;
    176     loop_ = NULL;
    177   }
    178 
    179   MessageLoop* loop() const { return loop_; }
    180   EventInjector* injector() const { return injector_; }
    181 
    182  private:
    183   MessageLoop* loop_;
    184   EventInjector* injector_;
    185   DISALLOW_COPY_AND_ASSIGN(MessagePumpGLibTest);
    186 };
    187 
    188 }  // namespace
    189 
    190 TEST_F(MessagePumpGLibTest, TestQuit) {
    191   // Checks that Quit works and that the basic infrastructure is working.
    192 
    193   // Quit from a task
    194   RunLoop().RunUntilIdle();
    195   EXPECT_EQ(0, injector()->processed_events());
    196 
    197   injector()->Reset();
    198   // Quit from an event
    199   injector()->AddEvent(0, MessageLoop::QuitWhenIdleClosure());
    200   loop()->Run();
    201   EXPECT_EQ(1, injector()->processed_events());
    202 }
    203 
    204 TEST_F(MessagePumpGLibTest, TestEventTaskInterleave) {
    205   // Checks that tasks posted by events are executed before the next event if
    206   // the posted task queue is empty.
    207   // MessageLoop doesn't make strong guarantees that it is the case, but the
    208   // current implementation ensures it and the tests below rely on it.
    209   // If changes cause this test to fail, it is reasonable to change it, but
    210   // TestWorkWhileWaitingForEvents and TestEventsWhileWaitingForWork have to be
    211   // changed accordingly, otherwise they can become flaky.
    212   injector()->AddEventAsTask(0, Bind(&DoNothing));
    213   Closure check_task =
    214       Bind(&ExpectProcessedEvents, Unretained(injector()), 2);
    215   Closure posted_task =
    216       Bind(&PostMessageLoopTask, FROM_HERE, check_task);
    217   injector()->AddEventAsTask(0, posted_task);
    218   injector()->AddEventAsTask(0, Bind(&DoNothing));
    219   injector()->AddEvent(0, MessageLoop::QuitWhenIdleClosure());
    220   loop()->Run();
    221   EXPECT_EQ(4, injector()->processed_events());
    222 
    223   injector()->Reset();
    224   injector()->AddEventAsTask(0, Bind(&DoNothing));
    225   check_task =
    226       Bind(&ExpectProcessedEvents, Unretained(injector()), 2);
    227   posted_task = Bind(&PostMessageLoopTask, FROM_HERE, check_task);
    228   injector()->AddEventAsTask(0, posted_task);
    229   injector()->AddEventAsTask(10, Bind(&DoNothing));
    230   injector()->AddEvent(0, MessageLoop::QuitWhenIdleClosure());
    231   loop()->Run();
    232   EXPECT_EQ(4, injector()->processed_events());
    233 }
    234 
    235 TEST_F(MessagePumpGLibTest, TestWorkWhileWaitingForEvents) {
    236   int task_count = 0;
    237   // Tests that we process tasks while waiting for new events.
    238   // The event queue is empty at first.
    239   for (int i = 0; i < 10; ++i) {
    240     loop()->PostTask(FROM_HERE, Bind(&IncrementInt, &task_count));
    241   }
    242   // After all the previous tasks have executed, enqueue an event that will
    243   // quit.
    244   loop()->PostTask(
    245       FROM_HERE,
    246       Bind(&EventInjector::AddEvent, Unretained(injector()), 0,
    247                  MessageLoop::QuitWhenIdleClosure()));
    248   loop()->Run();
    249   ASSERT_EQ(10, task_count);
    250   EXPECT_EQ(1, injector()->processed_events());
    251 
    252   // Tests that we process delayed tasks while waiting for new events.
    253   injector()->Reset();
    254   task_count = 0;
    255   for (int i = 0; i < 10; ++i) {
    256     loop()->PostDelayedTask(
    257         FROM_HERE,
    258         Bind(&IncrementInt, &task_count),
    259         TimeDelta::FromMilliseconds(10*i));
    260   }
    261   // After all the previous tasks have executed, enqueue an event that will
    262   // quit.
    263   // This relies on the fact that delayed tasks are executed in delay order.
    264   // That is verified in message_loop_unittest.cc.
    265   loop()->PostDelayedTask(
    266       FROM_HERE,
    267       Bind(&EventInjector::AddEvent, Unretained(injector()), 10,
    268                  MessageLoop::QuitWhenIdleClosure()),
    269       TimeDelta::FromMilliseconds(150));
    270   loop()->Run();
    271   ASSERT_EQ(10, task_count);
    272   EXPECT_EQ(1, injector()->processed_events());
    273 }
    274 
    275 TEST_F(MessagePumpGLibTest, TestEventsWhileWaitingForWork) {
    276   // Tests that we process events while waiting for work.
    277   // The event queue is empty at first.
    278   for (int i = 0; i < 10; ++i) {
    279     injector()->AddDummyEvent(0);
    280   }
    281   // After all the events have been processed, post a task that will check that
    282   // the events have been processed (note: the task executes after the event
    283   // that posted it has been handled, so we expect 11 at that point).
    284   Closure check_task =
    285       Bind(&ExpectProcessedEvents, Unretained(injector()), 11);
    286   Closure posted_task =
    287       Bind(&PostMessageLoopTask, FROM_HERE, check_task);
    288   injector()->AddEventAsTask(10, posted_task);
    289 
    290   // And then quit (relies on the condition tested by TestEventTaskInterleave).
    291   injector()->AddEvent(10, MessageLoop::QuitWhenIdleClosure());
    292   loop()->Run();
    293 
    294   EXPECT_EQ(12, injector()->processed_events());
    295 }
    296 
    297 namespace {
    298 
    299 // This class is a helper for the concurrent events / posted tasks test below.
    300 // It will quit the main loop once enough tasks and events have been processed,
    301 // while making sure there is always work to do and events in the queue.
    302 class ConcurrentHelper : public RefCounted<ConcurrentHelper>  {
    303  public:
    304   explicit ConcurrentHelper(EventInjector* injector)
    305       : injector_(injector),
    306         event_count_(kStartingEventCount),
    307         task_count_(kStartingTaskCount) {
    308   }
    309 
    310   void FromTask() {
    311     if (task_count_ > 0) {
    312       --task_count_;
    313     }
    314     if (task_count_ == 0 && event_count_ == 0) {
    315         MessageLoop::current()->QuitWhenIdle();
    316     } else {
    317       MessageLoop::current()->PostTask(
    318           FROM_HERE, Bind(&ConcurrentHelper::FromTask, this));
    319     }
    320   }
    321 
    322   void FromEvent() {
    323     if (event_count_ > 0) {
    324       --event_count_;
    325     }
    326     if (task_count_ == 0 && event_count_ == 0) {
    327         MessageLoop::current()->QuitWhenIdle();
    328     } else {
    329       injector_->AddEventAsTask(
    330           0, Bind(&ConcurrentHelper::FromEvent, this));
    331     }
    332   }
    333 
    334   int event_count() const { return event_count_; }
    335   int task_count() const { return task_count_; }
    336 
    337  private:
    338   friend class RefCounted<ConcurrentHelper>;
    339 
    340   ~ConcurrentHelper() {}
    341 
    342   static const int kStartingEventCount = 20;
    343   static const int kStartingTaskCount = 20;
    344 
    345   EventInjector* injector_;
    346   int event_count_;
    347   int task_count_;
    348 };
    349 
    350 }  // namespace
    351 
    352 TEST_F(MessagePumpGLibTest, TestConcurrentEventPostedTask) {
    353   // Tests that posted tasks don't starve events, nor the opposite.
    354   // We use the helper class above. We keep both event and posted task queues
    355   // full, the helper verifies that both tasks and events get processed.
    356   // If that is not the case, either event_count_ or task_count_ will not get
    357   // to 0, and MessageLoop::QuitWhenIdle() will never be called.
    358   scoped_refptr<ConcurrentHelper> helper = new ConcurrentHelper(injector());
    359 
    360   // Add 2 events to the queue to make sure it is always full (when we remove
    361   // the event before processing it).
    362   injector()->AddEventAsTask(
    363       0, Bind(&ConcurrentHelper::FromEvent, helper.get()));
    364   injector()->AddEventAsTask(
    365       0, Bind(&ConcurrentHelper::FromEvent, helper.get()));
    366 
    367   // Similarly post 2 tasks.
    368   loop()->PostTask(
    369       FROM_HERE, Bind(&ConcurrentHelper::FromTask, helper.get()));
    370   loop()->PostTask(
    371       FROM_HERE, Bind(&ConcurrentHelper::FromTask, helper.get()));
    372 
    373   loop()->Run();
    374   EXPECT_EQ(0, helper->event_count());
    375   EXPECT_EQ(0, helper->task_count());
    376 }
    377 
    378 namespace {
    379 
    380 void AddEventsAndDrainGLib(EventInjector* injector) {
    381   // Add a couple of dummy events
    382   injector->AddDummyEvent(0);
    383   injector->AddDummyEvent(0);
    384   // Then add an event that will quit the main loop.
    385   injector->AddEvent(0, MessageLoop::QuitWhenIdleClosure());
    386 
    387   // Post a couple of dummy tasks
    388   MessageLoop::current()->PostTask(FROM_HERE, Bind(&DoNothing));
    389   MessageLoop::current()->PostTask(FROM_HERE, Bind(&DoNothing));
    390 
    391   // Drain the events
    392   while (g_main_context_pending(NULL)) {
    393     g_main_context_iteration(NULL, FALSE);
    394   }
    395 }
    396 
    397 }  // namespace
    398 
    399 TEST_F(MessagePumpGLibTest, TestDrainingGLib) {
    400   // Tests that draining events using GLib works.
    401   loop()->PostTask(
    402       FROM_HERE,
    403       Bind(&AddEventsAndDrainGLib, Unretained(injector())));
    404   loop()->Run();
    405 
    406   EXPECT_EQ(3, injector()->processed_events());
    407 }
    408 
    409 
    410 namespace {
    411 
    412 #if defined(TOOLKIT_GTK)
    413 void AddEventsAndDrainGtk(EventInjector* injector) {
    414   // Add a couple of dummy events
    415   injector->AddDummyEvent(0);
    416   injector->AddDummyEvent(0);
    417   // Then add an event that will quit the main loop.
    418   injector->AddEvent(0, MessageLoop::QuitWhenIdleClosure());
    419 
    420   // Post a couple of dummy tasks
    421   MessageLoop::current()->PostTask(FROM_HERE, Bind(&DoNothing));
    422   MessageLoop::current()->PostTask(FROM_HERE, Bind(&DoNothing));
    423 
    424   // Drain the events
    425   while (gtk_events_pending()) {
    426     gtk_main_iteration();
    427   }
    428 }
    429 #endif
    430 
    431 }  // namespace
    432 
    433 #if defined(TOOLKIT_GTK)
    434 TEST_F(MessagePumpGLibTest, TestDrainingGtk) {
    435   // Tests that draining events using Gtk works.
    436   loop()->PostTask(
    437       FROM_HERE,
    438       Bind(&AddEventsAndDrainGtk, Unretained(injector())));
    439   loop()->Run();
    440 
    441   EXPECT_EQ(3, injector()->processed_events());
    442 }
    443 #endif
    444 
    445 namespace {
    446 
    447 // Helper class that lets us run the GLib message loop.
    448 class GLibLoopRunner : public RefCounted<GLibLoopRunner> {
    449  public:
    450   GLibLoopRunner() : quit_(false) { }
    451 
    452   void RunGLib() {
    453     while (!quit_) {
    454       g_main_context_iteration(NULL, TRUE);
    455     }
    456   }
    457 
    458   void RunLoop() {
    459 #if defined(TOOLKIT_GTK)
    460     while (!quit_) {
    461       gtk_main_iteration();
    462     }
    463 #else
    464     while (!quit_) {
    465       g_main_context_iteration(NULL, TRUE);
    466     }
    467 #endif
    468   }
    469 
    470   void Quit() {
    471     quit_ = true;
    472   }
    473 
    474   void Reset() {
    475     quit_ = false;
    476   }
    477 
    478  private:
    479   friend class RefCounted<GLibLoopRunner>;
    480 
    481   ~GLibLoopRunner() {}
    482 
    483   bool quit_;
    484 };
    485 
    486 void TestGLibLoopInternal(EventInjector* injector) {
    487   // Allow tasks to be processed from 'native' event loops.
    488   MessageLoop::current()->SetNestableTasksAllowed(true);
    489   scoped_refptr<GLibLoopRunner> runner = new GLibLoopRunner();
    490 
    491   int task_count = 0;
    492   // Add a couple of dummy events
    493   injector->AddDummyEvent(0);
    494   injector->AddDummyEvent(0);
    495   // Post a couple of dummy tasks
    496   MessageLoop::current()->PostTask(
    497       FROM_HERE, Bind(&IncrementInt, &task_count));
    498   MessageLoop::current()->PostTask(
    499       FROM_HERE, Bind(&IncrementInt, &task_count));
    500   // Delayed events
    501   injector->AddDummyEvent(10);
    502   injector->AddDummyEvent(10);
    503   // Delayed work
    504   MessageLoop::current()->PostDelayedTask(
    505       FROM_HERE,
    506       Bind(&IncrementInt, &task_count),
    507       TimeDelta::FromMilliseconds(30));
    508   MessageLoop::current()->PostDelayedTask(
    509       FROM_HERE,
    510       Bind(&GLibLoopRunner::Quit, runner.get()),
    511       TimeDelta::FromMilliseconds(40));
    512 
    513   // Run a nested, straight GLib message loop.
    514   runner->RunGLib();
    515 
    516   ASSERT_EQ(3, task_count);
    517   EXPECT_EQ(4, injector->processed_events());
    518   MessageLoop::current()->QuitWhenIdle();
    519 }
    520 
    521 void TestGtkLoopInternal(EventInjector* injector) {
    522   // Allow tasks to be processed from 'native' event loops.
    523   MessageLoop::current()->SetNestableTasksAllowed(true);
    524   scoped_refptr<GLibLoopRunner> runner = new GLibLoopRunner();
    525 
    526   int task_count = 0;
    527   // Add a couple of dummy events
    528   injector->AddDummyEvent(0);
    529   injector->AddDummyEvent(0);
    530   // Post a couple of dummy tasks
    531   MessageLoop::current()->PostTask(
    532       FROM_HERE, Bind(&IncrementInt, &task_count));
    533   MessageLoop::current()->PostTask(
    534       FROM_HERE, Bind(&IncrementInt, &task_count));
    535   // Delayed events
    536   injector->AddDummyEvent(10);
    537   injector->AddDummyEvent(10);
    538   // Delayed work
    539   MessageLoop::current()->PostDelayedTask(
    540       FROM_HERE,
    541       Bind(&IncrementInt, &task_count),
    542       TimeDelta::FromMilliseconds(30));
    543   MessageLoop::current()->PostDelayedTask(
    544       FROM_HERE,
    545       Bind(&GLibLoopRunner::Quit, runner.get()),
    546       TimeDelta::FromMilliseconds(40));
    547 
    548   // Run a nested, straight Gtk message loop.
    549   runner->RunLoop();
    550 
    551   ASSERT_EQ(3, task_count);
    552   EXPECT_EQ(4, injector->processed_events());
    553   MessageLoop::current()->QuitWhenIdle();
    554 }
    555 
    556 }  // namespace
    557 
    558 TEST_F(MessagePumpGLibTest, TestGLibLoop) {
    559   // Tests that events and posted tasks are correctly executed if the message
    560   // loop is not run by MessageLoop::Run() but by a straight GLib loop.
    561   // Note that in this case we don't make strong guarantees about niceness
    562   // between events and posted tasks.
    563   loop()->PostTask(
    564       FROM_HERE,
    565       Bind(&TestGLibLoopInternal, Unretained(injector())));
    566   loop()->Run();
    567 }
    568 
    569 TEST_F(MessagePumpGLibTest, TestGtkLoop) {
    570   // Tests that events and posted tasks are correctly executed if the message
    571   // loop is not run by MessageLoop::Run() but by a straight Gtk loop.
    572   // Note that in this case we don't make strong guarantees about niceness
    573   // between events and posted tasks.
    574   loop()->PostTask(
    575       FROM_HERE,
    576       Bind(&TestGtkLoopInternal, Unretained(injector())));
    577   loop()->Run();
    578 }
    579 
    580 }  // namespace base
    581