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