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