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