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