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