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
22namespace base {
23namespace {
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).
28class EventInjector {
29 public:
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
37  ~EventInjector() {
38    g_source_destroy(source_);
39    g_source_unref(source_);
40  }
41
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
50  bool HandleCheck() {
51    if (events_.empty())
52      return false;
53    return events_[0].time <= Time::NowFromSystemTime();
54  }
55
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.
70  void AddEvent(int delay_ms, const Closure& callback) {
71    AddEventHelper(delay_ms, callback, Closure());
72  }
73
74  void AddDummyEvent(int delay_ms) {
75    AddEventHelper(delay_ms, Closure(), Closure());
76  }
77
78  void AddEventAsTask(int delay_ms, const Closure& task) {
79    AddEventHelper(delay_ms, Closure(), task);
80  }
81
82  void Reset() {
83    processed_events_ = 0;
84    events_.clear();
85  }
86
87  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
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
113  static gboolean Prepare(GSource* source, gint* timeout_ms) {
114    *timeout_ms = static_cast<Source*>(source)->injector->HandlePrepare();
115    return FALSE;
116  }
117
118  static gboolean Check(GSource* source) {
119    return static_cast<Source*>(source)->injector->HandleCheck();
120  }
121
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
136GSourceFuncs EventInjector::SourceFuncs = {
137  EventInjector::Prepare,
138  EventInjector::Check,
139  EventInjector::Dispatch,
140  NULL
141};
142
143void IncrementInt(int *value) {
144  ++*value;
145}
146
147// Checks how many events have been processed by the injector.
148void ExpectProcessedEvents(EventInjector* injector, int count) {
149  EXPECT_EQ(injector->processed_events(), count);
150}
151
152// Posts a task on the current message loop.
153void PostMessageLoopTask(const tracked_objects::Location& from_here,
154                         const Closure& task) {
155  MessageLoop::current()->PostTask(from_here, task);
156}
157
158// Test fixture.
159class MessagePumpGLibTest : public testing::Test {
160 public:
161  MessagePumpGLibTest() : loop_(NULL), injector_(NULL) { }
162
163  // Overridden from testing::Test:
164  virtual void SetUp() OVERRIDE {
165    loop_ = new MessageLoop(MessageLoop::TYPE_UI);
166    injector_ = new EventInjector();
167  }
168  virtual void TearDown() OVERRIDE {
169    delete injector_;
170    injector_ = NULL;
171    delete loop_;
172    loop_ = NULL;
173  }
174
175  MessageLoop* loop() const { return loop_; }
176  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
186TEST_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
200TEST_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
231TEST_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
271TEST_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
293namespace {
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.
298class ConcurrentHelper : public RefCounted<ConcurrentHelper>  {
299 public:
300  explicit ConcurrentHelper(EventInjector* injector)
301      : injector_(injector),
302        event_count_(kStartingEventCount),
303        task_count_(kStartingTaskCount) {
304  }
305
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
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
330  int event_count() const { return event_count_; }
331  int task_count() const { return task_count_; }
332
333 private:
334  friend class RefCounted<ConcurrentHelper>;
335
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
348TEST_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
374namespace {
375
376void 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
395TEST_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
405namespace {
406
407// Helper class that lets us run the GLib message loop.
408class GLibLoopRunner : public RefCounted<GLibLoopRunner> {
409 public:
410  GLibLoopRunner() : quit_(false) { }
411
412  void RunGLib() {
413    while (!quit_) {
414      g_main_context_iteration(NULL, TRUE);
415    }
416  }
417
418  void RunLoop() {
419    while (!quit_) {
420      g_main_context_iteration(NULL, TRUE);
421    }
422  }
423
424  void Quit() {
425    quit_ = true;
426  }
427
428  void Reset() {
429    quit_ = false;
430  }
431
432 private:
433  friend class RefCounted<GLibLoopRunner>;
434
435  ~GLibLoopRunner() {}
436
437  bool quit_;
438};
439
440void 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
475void 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
512TEST_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
523TEST_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