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.
5 #include "base/message_loop/message_pump_glib.h"
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"
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).
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
);
38 g_source_destroy(source_
);
39 g_source_unref(source_
);
43 // If the queue is empty, block.
46 TimeDelta delta
= events_
[0].time
- Time::NowFromSystemTime();
47 return std::max(0, static_cast<int>(ceil(delta
.InMillisecondsF())));
53 return events_
[0].time
<= Time::NowFromSystemTime();
56 void HandleDispatch() {
59 Event event
= events_
[0];
60 events_
.erase(events_
.begin());
62 if (!event
.callback
.is_null())
64 else if (!event
.task
.is_null())
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());
74 void AddDummyEvent(int delay_ms
) {
75 AddEventHelper(delay_ms
, Closure(), Closure());
78 void AddEventAsTask(int delay_ms
, const Closure
& task
) {
79 AddEventHelper(delay_ms
, Closure(), task
);
83 processed_events_
= 0;
87 int processed_events() const { return processed_events_
; }
96 struct Source
: public GSource
{
97 EventInjector
* injector
;
101 int delay_ms
, const Closure
& callback
, const Closure
& task
) {
103 if (!events_
.empty())
104 last_time
= (events_
.end()-1)->time
;
106 last_time
= Time::NowFromSystemTime();
108 Time future
= last_time
+ TimeDelta::FromMilliseconds(delay_ms
);
109 EventInjector::Event event
= {future
, callback
, task
};
110 events_
.push_back(event
);
113 static gboolean
Prepare(GSource
* source
, gint
* timeout_ms
) {
114 *timeout_ms
= static_cast<Source
*>(source
)->injector
->HandlePrepare();
118 static gboolean
Check(GSource
* source
) {
119 return static_cast<Source
*>(source
)->injector
->HandleCheck();
122 static gboolean
Dispatch(GSource
* source
,
123 GSourceFunc unused_func
,
124 gpointer unused_data
) {
125 static_cast<Source
*>(source
)->injector
->HandleDispatch();
130 std::vector
<Event
> events_
;
131 int processed_events_
;
132 static GSourceFuncs SourceFuncs
;
133 DISALLOW_COPY_AND_ASSIGN(EventInjector
);
136 GSourceFuncs
EventInjector::SourceFuncs
= {
137 EventInjector::Prepare
,
138 EventInjector::Check
,
139 EventInjector::Dispatch
,
143 void IncrementInt(int *value
) {
147 // Checks how many events have been processed by the injector.
148 void ExpectProcessedEvents(EventInjector
* injector
, int count
) {
149 EXPECT_EQ(injector
->processed_events(), count
);
152 // Posts a task on the current message loop.
153 void PostMessageLoopTask(const tracked_objects::Location
& from_here
,
154 const Closure
& task
) {
155 MessageLoop::current()->PostTask(from_here
, task
);
159 class MessagePumpGLibTest
: public testing::Test
{
161 MessagePumpGLibTest() : loop_(NULL
), injector_(NULL
) { }
163 // Overridden from testing::Test:
164 void SetUp() override
{
165 loop_
= new MessageLoop(MessageLoop::TYPE_UI
);
166 injector_
= new EventInjector();
168 void TearDown() override
{
175 MessageLoop
* loop() const { return loop_
; }
176 EventInjector
* injector() const { return injector_
; }
180 EventInjector
* injector_
;
181 DISALLOW_COPY_AND_ASSIGN(MessagePumpGLibTest
);
186 TEST_F(MessagePumpGLibTest
, TestQuit
) {
187 // Checks that Quit works and that the basic infrastructure is working.
190 RunLoop().RunUntilIdle();
191 EXPECT_EQ(0, injector()->processed_events());
194 // Quit from an event
195 injector()->AddEvent(0, MessageLoop::QuitWhenIdleClosure());
197 EXPECT_EQ(1, injector()->processed_events());
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
));
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());
217 EXPECT_EQ(4, injector()->processed_events());
220 injector()->AddEventAsTask(0, Bind(&DoNothing
));
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());
228 EXPECT_EQ(4, injector()->processed_events());
231 TEST_F(MessagePumpGLibTest
, TestWorkWhileWaitingForEvents
) {
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
));
238 // After all the previous tasks have executed, enqueue an event that will
242 Bind(&EventInjector::AddEvent
, Unretained(injector()), 0,
243 MessageLoop::QuitWhenIdleClosure()));
245 ASSERT_EQ(10, task_count
);
246 EXPECT_EQ(1, injector()->processed_events());
248 // Tests that we process delayed tasks while waiting for new events.
251 for (int i
= 0; i
< 10; ++i
) {
252 loop()->PostDelayedTask(
254 Bind(&IncrementInt
, &task_count
),
255 TimeDelta::FromMilliseconds(10*i
));
257 // After all the previous tasks have executed, enqueue an event that will
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(
263 Bind(&EventInjector::AddEvent
, Unretained(injector()), 10,
264 MessageLoop::QuitWhenIdleClosure()),
265 TimeDelta::FromMilliseconds(150));
267 ASSERT_EQ(10, task_count
);
268 EXPECT_EQ(1, injector()->processed_events());
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);
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).
281 Bind(&ExpectProcessedEvents
, Unretained(injector()), 11);
282 Closure posted_task
=
283 Bind(&PostMessageLoopTask
, FROM_HERE
, check_task
);
284 injector()->AddEventAsTask(10, posted_task
);
286 // And then quit (relies on the condition tested by TestEventTaskInterleave).
287 injector()->AddEvent(10, MessageLoop::QuitWhenIdleClosure());
290 EXPECT_EQ(12, injector()->processed_events());
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
> {
300 explicit ConcurrentHelper(EventInjector
* injector
)
301 : injector_(injector
),
302 event_count_(kStartingEventCount
),
303 task_count_(kStartingTaskCount
) {
307 if (task_count_
> 0) {
310 if (task_count_
== 0 && event_count_
== 0) {
311 MessageLoop::current()->QuitWhenIdle();
313 MessageLoop::current()->PostTask(
314 FROM_HERE
, Bind(&ConcurrentHelper::FromTask
, this));
319 if (event_count_
> 0) {
322 if (task_count_
== 0 && event_count_
== 0) {
323 MessageLoop::current()->QuitWhenIdle();
325 injector_
->AddEventAsTask(
326 0, Bind(&ConcurrentHelper::FromEvent
, this));
330 int event_count() const { return event_count_
; }
331 int task_count() const { return task_count_
; }
334 friend class RefCounted
<ConcurrentHelper
>;
336 ~ConcurrentHelper() {}
338 static const int kStartingEventCount
= 20;
339 static const int kStartingTaskCount
= 20;
341 EventInjector
* injector_
;
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());
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()));
363 // Similarly post 2 tasks.
365 FROM_HERE
, Bind(&ConcurrentHelper::FromTask
, helper
.get()));
367 FROM_HERE
, Bind(&ConcurrentHelper::FromTask
, helper
.get()));
370 EXPECT_EQ(0, helper
->event_count());
371 EXPECT_EQ(0, helper
->task_count());
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());
383 // Post a couple of dummy tasks
384 MessageLoop::current()->PostTask(FROM_HERE
, Bind(&DoNothing
));
385 MessageLoop::current()->PostTask(FROM_HERE
, Bind(&DoNothing
));
388 while (g_main_context_pending(NULL
)) {
389 g_main_context_iteration(NULL
, FALSE
);
395 TEST_F(MessagePumpGLibTest
, TestDrainingGLib
) {
396 // Tests that draining events using GLib works.
399 Bind(&AddEventsAndDrainGLib
, Unretained(injector())));
402 EXPECT_EQ(3, injector()->processed_events());
407 // Helper class that lets us run the GLib message loop.
408 class GLibLoopRunner
: public RefCounted
<GLibLoopRunner
> {
410 GLibLoopRunner() : quit_(false) { }
414 g_main_context_iteration(NULL
, TRUE
);
420 g_main_context_iteration(NULL
, TRUE
);
433 friend class RefCounted
<GLibLoopRunner
>;
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();
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
));
455 injector
->AddDummyEvent(10);
456 injector
->AddDummyEvent(10);
458 MessageLoop::current()->PostDelayedTask(
460 Bind(&IncrementInt
, &task_count
),
461 TimeDelta::FromMilliseconds(30));
462 MessageLoop::current()->PostDelayedTask(
464 Bind(&GLibLoopRunner::Quit
, runner
.get()),
465 TimeDelta::FromMilliseconds(40));
467 // Run a nested, straight GLib message loop.
470 ASSERT_EQ(3, task_count
);
471 EXPECT_EQ(4, injector
->processed_events());
472 MessageLoop::current()->QuitWhenIdle();
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();
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
));
490 injector
->AddDummyEvent(10);
491 injector
->AddDummyEvent(10);
493 MessageLoop::current()->PostDelayedTask(
495 Bind(&IncrementInt
, &task_count
),
496 TimeDelta::FromMilliseconds(30));
497 MessageLoop::current()->PostDelayedTask(
499 Bind(&GLibLoopRunner::Quit
, runner
.get()),
500 TimeDelta::FromMilliseconds(40));
502 // Run a nested, straight Gtk message loop.
505 ASSERT_EQ(3, task_count
);
506 EXPECT_EQ(4, injector
->processed_events());
507 MessageLoop::current()->QuitWhenIdle();
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.
519 Bind(&TestGLibLoopInternal
, Unretained(injector())));
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.
530 Bind(&TestGtkLoopInternal
, Unretained(injector())));