blob: 1599847324ceaa56bfe14406f89a244cec6390c8 [file] [log] [blame]
[email protected]ac4c6682012-01-04 00:57:391// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]b44d5cc2009-06-15 10:30:442// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]59e69e742013-06-18 20:27:525#include "base/message_loop/message_pump_glib.h"
[email protected]b44d5cc2009-06-15 10:30:446
[email protected]d90efd52012-06-28 19:57:267#include <glib.h>
[email protected]23d35392009-06-15 19:53:088#include <math.h>
9
[email protected]b44d5cc2009-06-15 10:30:4410#include <algorithm>
11#include <vector>
[email protected]23d35392009-06-15 19:53:0812
[email protected]8f5a7e492012-01-01 02:14:4713#include "base/bind.h"
14#include "base/bind_helpers.h"
[email protected]763839e2011-12-09 23:06:0215#include "base/callback.h"
Nick Diego Yamane58914192019-08-08 17:12:4816#include "base/files/file_util.h"
Hans Wennborgafeb3902020-06-17 14:42:2917#include "base/logging.h"
avi9b6f42932015-12-26 22:15:1418#include "base/macros.h"
Carlos Caballerof5b6f91c2019-12-20 14:07:3819#include "base/memory/ptr_util.h"
[email protected]3b63f8f42011-03-28 01:54:1520#include "base/memory/ref_counted.h"
Carlos Caballerodd8bf7b042019-07-30 14:14:1521#include "base/message_loop/message_pump_type.h"
Nick Diego Yamane58914192019-08-08 17:12:4822#include "base/posix/eintr_wrapper.h"
[email protected]7ff48ca2013-02-06 16:56:1923#include "base/run_loop.h"
fdoray6ef45cf2016-08-25 15:36:3724#include "base/single_thread_task_runner.h"
Nick Diego Yamane58914192019-08-08 17:12:4825#include "base/synchronization/waitable_event.h"
26#include "base/synchronization/waitable_event_watcher.h"
Carlos Caballerob25fe8472020-07-17 10:27:1727#include "base/task/current_thread.h"
Carlos Caballerof5b6f91c2019-12-20 14:07:3828#include "base/task/single_thread_task_executor.h"
29#include "base/test/task_environment.h"
[email protected]34b99632011-01-01 01:01:0630#include "base/threading/thread.h"
fdoray2df4a9e2016-07-18 23:47:1631#include "base/threading/thread_task_runner_handle.h"
[email protected]b44d5cc2009-06-15 10:30:4432#include "testing/gtest/include/gtest/gtest.h"
33
[email protected]7ff48ca2013-02-06 16:56:1934namespace base {
[email protected]b44d5cc2009-06-15 10:30:4435namespace {
36
37// This class injects dummy "events" into the GLib loop. When "handled" these
38// events can run tasks. This is intended to mock gtk events (the corresponding
39// GLib source runs at the same priority).
40class EventInjector {
41 public:
42 EventInjector() : processed_events_(0) {
43 source_ = static_cast<Source*>(g_source_new(&SourceFuncs, sizeof(Source)));
44 source_->injector = this;
Ivan Kotenkova16212a52017-11-08 12:37:3345 g_source_attach(source_, nullptr);
[email protected]b44d5cc2009-06-15 10:30:4446 g_source_set_can_recurse(source_, TRUE);
47 }
48
49 ~EventInjector() {
50 g_source_destroy(source_);
51 g_source_unref(source_);
52 }
53
54 int HandlePrepare() {
55 // If the queue is empty, block.
56 if (events_.empty())
57 return -1;
[email protected]59e69e742013-06-18 20:27:5258 TimeDelta delta = events_[0].time - Time::NowFromSystemTime();
[email protected]b44d5cc2009-06-15 10:30:4459 return std::max(0, static_cast<int>(ceil(delta.InMillisecondsF())));
60 }
61
62 bool HandleCheck() {
63 if (events_.empty())
64 return false;
[email protected]59e69e742013-06-18 20:27:5265 return events_[0].time <= Time::NowFromSystemTime();
[email protected]b44d5cc2009-06-15 10:30:4466 }
67
68 void HandleDispatch() {
69 if (events_.empty())
70 return;
tzika8cc2202017-04-18 07:01:1571 Event event = std::move(events_[0]);
[email protected]b44d5cc2009-06-15 10:30:4472 events_.erase(events_.begin());
73 ++processed_events_;
[email protected]8f5a7e492012-01-01 02:14:4774 if (!event.callback.is_null())
tzika8cc2202017-04-18 07:01:1575 std::move(event.callback).Run();
[email protected]8f5a7e492012-01-01 02:14:4776 else if (!event.task.is_null())
tzika8cc2202017-04-18 07:01:1577 std::move(event.task).Run();
[email protected]b44d5cc2009-06-15 10:30:4478 }
79
[email protected]763839e2011-12-09 23:06:0280 // Adds an event to the queue. When "handled", executes |callback|.
[email protected]b44d5cc2009-06-15 10:30:4481 // delay_ms is relative to the last event if any, or to Now() otherwise.
tzika8cc2202017-04-18 07:01:1582 void AddEvent(int delay_ms, OnceClosure callback) {
83 AddEventHelper(delay_ms, std::move(callback), OnceClosure());
[email protected]763839e2011-12-09 23:06:0284 }
85
86 void AddDummyEvent(int delay_ms) {
tzika8cc2202017-04-18 07:01:1587 AddEventHelper(delay_ms, OnceClosure(), OnceClosure());
[email protected]763839e2011-12-09 23:06:0288 }
89
tzika8cc2202017-04-18 07:01:1590 void AddEventAsTask(int delay_ms, OnceClosure task) {
91 AddEventHelper(delay_ms, OnceClosure(), std::move(task));
[email protected]b44d5cc2009-06-15 10:30:4492 }
93
94 void Reset() {
95 processed_events_ = 0;
96 events_.clear();
97 }
98
99 int processed_events() const { return processed_events_; }
100
101 private:
102 struct Event {
[email protected]59e69e742013-06-18 20:27:52103 Time time;
tzika8cc2202017-04-18 07:01:15104 OnceClosure callback;
105 OnceClosure task;
[email protected]b44d5cc2009-06-15 10:30:44106 };
107
108 struct Source : public GSource {
109 EventInjector* injector;
110 };
111
tzika8cc2202017-04-18 07:01:15112 void AddEventHelper(int delay_ms, OnceClosure callback, OnceClosure task) {
[email protected]59e69e742013-06-18 20:27:52113 Time last_time;
[email protected]8f5a7e492012-01-01 02:14:47114 if (!events_.empty())
[email protected]763839e2011-12-09 23:06:02115 last_time = (events_.end()-1)->time;
[email protected]8f5a7e492012-01-01 02:14:47116 else
[email protected]59e69e742013-06-18 20:27:52117 last_time = Time::NowFromSystemTime();
[email protected]8f5a7e492012-01-01 02:14:47118
[email protected]59e69e742013-06-18 20:27:52119 Time future = last_time + TimeDelta::FromMilliseconds(delay_ms);
tzika8cc2202017-04-18 07:01:15120 EventInjector::Event event = {future, std::move(callback), std::move(task)};
121 events_.push_back(std::move(event));
[email protected]763839e2011-12-09 23:06:02122 }
123
[email protected]b44d5cc2009-06-15 10:30:44124 static gboolean Prepare(GSource* source, gint* timeout_ms) {
125 *timeout_ms = static_cast<Source*>(source)->injector->HandlePrepare();
126 return FALSE;
127 }
128
129 static gboolean Check(GSource* source) {
130 return static_cast<Source*>(source)->injector->HandleCheck();
131 }
132
133 static gboolean Dispatch(GSource* source,
134 GSourceFunc unused_func,
135 gpointer unused_data) {
136 static_cast<Source*>(source)->injector->HandleDispatch();
137 return TRUE;
138 }
139
140 Source* source_;
141 std::vector<Event> events_;
142 int processed_events_;
143 static GSourceFuncs SourceFuncs;
144 DISALLOW_COPY_AND_ASSIGN(EventInjector);
145};
146
Ivan Kotenkova16212a52017-11-08 12:37:33147GSourceFuncs EventInjector::SourceFuncs = {EventInjector::Prepare,
148 EventInjector::Check,
149 EventInjector::Dispatch, nullptr};
[email protected]b44d5cc2009-06-15 10:30:44150
[email protected]b44d5cc2009-06-15 10:30:44151void IncrementInt(int *value) {
152 ++*value;
153}
154
155// Checks how many events have been processed by the injector.
156void ExpectProcessedEvents(EventInjector* injector, int count) {
157 EXPECT_EQ(injector->processed_events(), count);
158}
159
[email protected]b44d5cc2009-06-15 10:30:44160// Posts a task on the current message loop.
Brett Wilson8e88b312017-09-12 05:22:16161void PostMessageLoopTask(const Location& from_here, OnceClosure task) {
tzika8cc2202017-04-18 07:01:15162 ThreadTaskRunnerHandle::Get()->PostTask(from_here, std::move(task));
[email protected]b44d5cc2009-06-15 10:30:44163}
164
165// Test fixture.
166class MessagePumpGLibTest : public testing::Test {
167 public:
Carlos Caballerof5b6f91c2019-12-20 14:07:38168 MessagePumpGLibTest() = default;
169 EventInjector* injector() { return &injector_; }
[email protected]b44d5cc2009-06-15 10:30:44170
171 private:
Carlos Caballerof5b6f91c2019-12-20 14:07:38172 test::SingleThreadTaskEnvironment task_environment_{
173 test::SingleThreadTaskEnvironment::MainThreadType::UI};
174 EventInjector injector_;
175
[email protected]b44d5cc2009-06-15 10:30:44176 DISALLOW_COPY_AND_ASSIGN(MessagePumpGLibTest);
177};
178
179} // namespace
180
[email protected]b44d5cc2009-06-15 10:30:44181TEST_F(MessagePumpGLibTest, TestQuit) {
182 // Checks that Quit works and that the basic infrastructure is working.
183
184 // Quit from a task
[email protected]7ff48ca2013-02-06 16:56:19185 RunLoop().RunUntilIdle();
[email protected]b44d5cc2009-06-15 10:30:44186 EXPECT_EQ(0, injector()->processed_events());
187
188 injector()->Reset();
189 // Quit from an event
Wezfda077b2018-06-07 21:18:11190 RunLoop run_loop;
191 injector()->AddEvent(0, run_loop.QuitClosure());
192 run_loop.Run();
[email protected]b44d5cc2009-06-15 10:30:44193 EXPECT_EQ(1, injector()->processed_events());
194}
195
196TEST_F(MessagePumpGLibTest, TestEventTaskInterleave) {
197 // Checks that tasks posted by events are executed before the next event if
198 // the posted task queue is empty.
199 // MessageLoop doesn't make strong guarantees that it is the case, but the
200 // current implementation ensures it and the tests below rely on it.
201 // If changes cause this test to fail, it is reasonable to change it, but
202 // TestWorkWhileWaitingForEvents and TestEventsWhileWaitingForWork have to be
203 // changed accordingly, otherwise they can become flaky.
Peter Kasting341e1fb2018-02-24 00:03:01204 injector()->AddEventAsTask(0, DoNothing());
tzika8cc2202017-04-18 07:01:15205 OnceClosure check_task =
206 BindOnce(&ExpectProcessedEvents, Unretained(injector()), 2);
207 OnceClosure posted_task =
208 BindOnce(&PostMessageLoopTask, FROM_HERE, std::move(check_task));
209 injector()->AddEventAsTask(0, std::move(posted_task));
Peter Kasting341e1fb2018-02-24 00:03:01210 injector()->AddEventAsTask(0, DoNothing());
Wezfda077b2018-06-07 21:18:11211 {
212 RunLoop run_loop;
213 injector()->AddEvent(0, run_loop.QuitClosure());
214 run_loop.Run();
215 }
[email protected]b44d5cc2009-06-15 10:30:44216 EXPECT_EQ(4, injector()->processed_events());
217
218 injector()->Reset();
Peter Kasting341e1fb2018-02-24 00:03:01219 injector()->AddEventAsTask(0, DoNothing());
tzika8cc2202017-04-18 07:01:15220 check_task = BindOnce(&ExpectProcessedEvents, Unretained(injector()), 2);
221 posted_task =
222 BindOnce(&PostMessageLoopTask, FROM_HERE, std::move(check_task));
223 injector()->AddEventAsTask(0, std::move(posted_task));
Peter Kasting341e1fb2018-02-24 00:03:01224 injector()->AddEventAsTask(10, DoNothing());
Wezfda077b2018-06-07 21:18:11225 {
226 RunLoop run_loop;
227 injector()->AddEvent(0, run_loop.QuitClosure());
228 run_loop.Run();
229 }
[email protected]b44d5cc2009-06-15 10:30:44230 EXPECT_EQ(4, injector()->processed_events());
231}
232
233TEST_F(MessagePumpGLibTest, TestWorkWhileWaitingForEvents) {
234 int task_count = 0;
235 // Tests that we process tasks while waiting for new events.
236 // The event queue is empty at first.
237 for (int i = 0; i < 10; ++i) {
Carlos Caballerof5b6f91c2019-12-20 14:07:38238 ThreadTaskRunnerHandle::Get()->PostTask(
239 FROM_HERE, BindOnce(&IncrementInt, &task_count));
[email protected]b44d5cc2009-06-15 10:30:44240 }
241 // After all the previous tasks have executed, enqueue an event that will
242 // quit.
Wezfda077b2018-06-07 21:18:11243 {
244 RunLoop run_loop;
Carlos Caballerof5b6f91c2019-12-20 14:07:38245 ThreadTaskRunnerHandle::Get()->PostTask(
Wezfda077b2018-06-07 21:18:11246 FROM_HERE, BindOnce(&EventInjector::AddEvent, Unretained(injector()), 0,
247 run_loop.QuitClosure()));
248 run_loop.Run();
249 }
[email protected]b44d5cc2009-06-15 10:30:44250 ASSERT_EQ(10, task_count);
251 EXPECT_EQ(1, injector()->processed_events());
252
253 // Tests that we process delayed tasks while waiting for new events.
254 injector()->Reset();
255 task_count = 0;
256 for (int i = 0; i < 10; ++i) {
Carlos Caballerof5b6f91c2019-12-20 14:07:38257 ThreadTaskRunnerHandle::Get()->PostDelayedTask(
258 FROM_HERE, BindOnce(&IncrementInt, &task_count),
259 TimeDelta::FromMilliseconds(10 * i));
[email protected]b44d5cc2009-06-15 10:30:44260 }
261 // After all the previous tasks have executed, enqueue an event that will
262 // quit.
263 // This relies on the fact that delayed tasks are executed in delay order.
264 // That is verified in message_loop_unittest.cc.
Wezfda077b2018-06-07 21:18:11265 {
266 RunLoop run_loop;
Carlos Caballerof5b6f91c2019-12-20 14:07:38267 ThreadTaskRunnerHandle::Get()->PostDelayedTask(
Wezfda077b2018-06-07 21:18:11268 FROM_HERE,
269 BindOnce(&EventInjector::AddEvent, Unretained(injector()), 0,
270 run_loop.QuitClosure()),
271 TimeDelta::FromMilliseconds(150));
272 run_loop.Run();
273 }
[email protected]b44d5cc2009-06-15 10:30:44274 ASSERT_EQ(10, task_count);
275 EXPECT_EQ(1, injector()->processed_events());
276}
277
278TEST_F(MessagePumpGLibTest, TestEventsWhileWaitingForWork) {
279 // Tests that we process events while waiting for work.
280 // The event queue is empty at first.
281 for (int i = 0; i < 10; ++i) {
[email protected]763839e2011-12-09 23:06:02282 injector()->AddDummyEvent(0);
[email protected]b44d5cc2009-06-15 10:30:44283 }
284 // After all the events have been processed, post a task that will check that
285 // the events have been processed (note: the task executes after the event
286 // that posted it has been handled, so we expect 11 at that point).
tzika8cc2202017-04-18 07:01:15287 OnceClosure check_task =
288 BindOnce(&ExpectProcessedEvents, Unretained(injector()), 11);
289 OnceClosure posted_task =
290 BindOnce(&PostMessageLoopTask, FROM_HERE, std::move(check_task));
291 injector()->AddEventAsTask(10, std::move(posted_task));
[email protected]b44d5cc2009-06-15 10:30:44292
293 // And then quit (relies on the condition tested by TestEventTaskInterleave).
Wezfda077b2018-06-07 21:18:11294 RunLoop run_loop;
295 injector()->AddEvent(10, run_loop.QuitClosure());
296 run_loop.Run();
[email protected]b44d5cc2009-06-15 10:30:44297
298 EXPECT_EQ(12, injector()->processed_events());
299}
300
301namespace {
302
303// This class is a helper for the concurrent events / posted tasks test below.
304// It will quit the main loop once enough tasks and events have been processed,
305// while making sure there is always work to do and events in the queue.
[email protected]59e69e742013-06-18 20:27:52306class ConcurrentHelper : public RefCounted<ConcurrentHelper> {
[email protected]b44d5cc2009-06-15 10:30:44307 public:
Wezfda077b2018-06-07 21:18:11308 ConcurrentHelper(EventInjector* injector, OnceClosure done_closure)
[email protected]b44d5cc2009-06-15 10:30:44309 : injector_(injector),
Wezfda077b2018-06-07 21:18:11310 done_closure_(std::move(done_closure)),
[email protected]b44d5cc2009-06-15 10:30:44311 event_count_(kStartingEventCount),
Wezfda077b2018-06-07 21:18:11312 task_count_(kStartingTaskCount) {}
[email protected]b44d5cc2009-06-15 10:30:44313
314 void FromTask() {
315 if (task_count_ > 0) {
316 --task_count_;
317 }
318 if (task_count_ == 0 && event_count_ == 0) {
Wezfda077b2018-06-07 21:18:11319 std::move(done_closure_).Run();
[email protected]b44d5cc2009-06-15 10:30:44320 } else {
fdoray2df4a9e2016-07-18 23:47:16321 ThreadTaskRunnerHandle::Get()->PostTask(
tzik92b7a422017-04-11 15:00:44322 FROM_HERE, BindOnce(&ConcurrentHelper::FromTask, this));
[email protected]b44d5cc2009-06-15 10:30:44323 }
324 }
325
326 void FromEvent() {
327 if (event_count_ > 0) {
328 --event_count_;
329 }
330 if (task_count_ == 0 && event_count_ == 0) {
Wezfda077b2018-06-07 21:18:11331 std::move(done_closure_).Run();
[email protected]b44d5cc2009-06-15 10:30:44332 } else {
tzika8cc2202017-04-18 07:01:15333 injector_->AddEventAsTask(0,
334 BindOnce(&ConcurrentHelper::FromEvent, this));
[email protected]b44d5cc2009-06-15 10:30:44335 }
336 }
337
338 int event_count() const { return event_count_; }
339 int task_count() const { return task_count_; }
340
341 private:
[email protected]59e69e742013-06-18 20:27:52342 friend class RefCounted<ConcurrentHelper>;
[email protected]877d55d2009-11-05 21:53:08343
344 ~ConcurrentHelper() {}
345
[email protected]b44d5cc2009-06-15 10:30:44346 static const int kStartingEventCount = 20;
347 static const int kStartingTaskCount = 20;
348
349 EventInjector* injector_;
Wezfda077b2018-06-07 21:18:11350 OnceClosure done_closure_;
[email protected]b44d5cc2009-06-15 10:30:44351 int event_count_;
352 int task_count_;
353};
354
355} // namespace
356
357TEST_F(MessagePumpGLibTest, TestConcurrentEventPostedTask) {
358 // Tests that posted tasks don't starve events, nor the opposite.
359 // We use the helper class above. We keep both event and posted task queues
360 // full, the helper verifies that both tasks and events get processed.
361 // If that is not the case, either event_count_ or task_count_ will not get
[email protected]91cae2592013-01-10 14:56:17362 // to 0, and MessageLoop::QuitWhenIdle() will never be called.
Wezfda077b2018-06-07 21:18:11363 RunLoop run_loop;
364 scoped_refptr<ConcurrentHelper> helper =
365 new ConcurrentHelper(injector(), run_loop.QuitClosure());
[email protected]b44d5cc2009-06-15 10:30:44366
367 // Add 2 events to the queue to make sure it is always full (when we remove
368 // the event before processing it).
tzika8cc2202017-04-18 07:01:15369 injector()->AddEventAsTask(0, BindOnce(&ConcurrentHelper::FromEvent, helper));
370 injector()->AddEventAsTask(0, BindOnce(&ConcurrentHelper::FromEvent, helper));
[email protected]b44d5cc2009-06-15 10:30:44371
372 // Similarly post 2 tasks.
Carlos Caballerof5b6f91c2019-12-20 14:07:38373 ThreadTaskRunnerHandle::Get()->PostTask(
tzik92b7a422017-04-11 15:00:44374 FROM_HERE, BindOnce(&ConcurrentHelper::FromTask, helper));
Carlos Caballerof5b6f91c2019-12-20 14:07:38375 ThreadTaskRunnerHandle::Get()->PostTask(
tzik92b7a422017-04-11 15:00:44376 FROM_HERE, BindOnce(&ConcurrentHelper::FromTask, helper));
[email protected]b44d5cc2009-06-15 10:30:44377
Wezfda077b2018-06-07 21:18:11378 run_loop.Run();
[email protected]b44d5cc2009-06-15 10:30:44379 EXPECT_EQ(0, helper->event_count());
380 EXPECT_EQ(0, helper->task_count());
381}
382
383namespace {
384
Wezfda077b2018-06-07 21:18:11385void AddEventsAndDrainGLib(EventInjector* injector, OnceClosure on_drained) {
[email protected]b44d5cc2009-06-15 10:30:44386 // Add a couple of dummy events
[email protected]763839e2011-12-09 23:06:02387 injector->AddDummyEvent(0);
388 injector->AddDummyEvent(0);
[email protected]b44d5cc2009-06-15 10:30:44389 // Then add an event that will quit the main loop.
Wezfda077b2018-06-07 21:18:11390 injector->AddEvent(0, std::move(on_drained));
[email protected]b44d5cc2009-06-15 10:30:44391
392 // Post a couple of dummy tasks
Peter Kasting341e1fb2018-02-24 00:03:01393 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, DoNothing());
394 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, DoNothing());
[email protected]b44d5cc2009-06-15 10:30:44395
396 // Drain the events
Ivan Kotenkova16212a52017-11-08 12:37:33397 while (g_main_context_pending(nullptr)) {
398 g_main_context_iteration(nullptr, FALSE);
[email protected]b44d5cc2009-06-15 10:30:44399 }
400}
401
402} // namespace
403
404TEST_F(MessagePumpGLibTest, TestDrainingGLib) {
405 // Tests that draining events using GLib works.
Wezfda077b2018-06-07 21:18:11406 RunLoop run_loop;
Carlos Caballerof5b6f91c2019-12-20 14:07:38407 ThreadTaskRunnerHandle::Get()->PostTask(
Wezfda077b2018-06-07 21:18:11408 FROM_HERE, BindOnce(&AddEventsAndDrainGLib, Unretained(injector()),
409 run_loop.QuitClosure()));
410 run_loop.Run();
[email protected]b44d5cc2009-06-15 10:30:44411
412 EXPECT_EQ(3, injector()->processed_events());
413}
414
[email protected]b44d5cc2009-06-15 10:30:44415namespace {
416
417// Helper class that lets us run the GLib message loop.
[email protected]59e69e742013-06-18 20:27:52418class GLibLoopRunner : public RefCounted<GLibLoopRunner> {
[email protected]b44d5cc2009-06-15 10:30:44419 public:
420 GLibLoopRunner() : quit_(false) { }
421
422 void RunGLib() {
423 while (!quit_) {
Ivan Kotenkova16212a52017-11-08 12:37:33424 g_main_context_iteration(nullptr, TRUE);
[email protected]b44d5cc2009-06-15 10:30:44425 }
426 }
427
[email protected]258dca42011-09-21 00:17:19428 void RunLoop() {
[email protected]258dca42011-09-21 00:17:19429 while (!quit_) {
Ivan Kotenkova16212a52017-11-08 12:37:33430 g_main_context_iteration(nullptr, TRUE);
[email protected]258dca42011-09-21 00:17:19431 }
[email protected]b44d5cc2009-06-15 10:30:44432 }
433
434 void Quit() {
435 quit_ = true;
436 }
437
438 void Reset() {
439 quit_ = false;
440 }
441
442 private:
[email protected]59e69e742013-06-18 20:27:52443 friend class RefCounted<GLibLoopRunner>;
[email protected]877d55d2009-11-05 21:53:08444
445 ~GLibLoopRunner() {}
446
[email protected]b44d5cc2009-06-15 10:30:44447 bool quit_;
448};
449
Wezfda077b2018-06-07 21:18:11450void TestGLibLoopInternal(EventInjector* injector, OnceClosure done) {
[email protected]b44d5cc2009-06-15 10:30:44451 scoped_refptr<GLibLoopRunner> runner = new GLibLoopRunner();
452
453 int task_count = 0;
454 // Add a couple of dummy events
[email protected]763839e2011-12-09 23:06:02455 injector->AddDummyEvent(0);
456 injector->AddDummyEvent(0);
[email protected]b44d5cc2009-06-15 10:30:44457 // Post a couple of dummy tasks
fdoray2df4a9e2016-07-18 23:47:16458 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
tzik92b7a422017-04-11 15:00:44459 BindOnce(&IncrementInt, &task_count));
fdoray2df4a9e2016-07-18 23:47:16460 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
tzik92b7a422017-04-11 15:00:44461 BindOnce(&IncrementInt, &task_count));
[email protected]b44d5cc2009-06-15 10:30:44462 // Delayed events
[email protected]763839e2011-12-09 23:06:02463 injector->AddDummyEvent(10);
464 injector->AddDummyEvent(10);
[email protected]b44d5cc2009-06-15 10:30:44465 // Delayed work
fdoray2df4a9e2016-07-18 23:47:16466 ThreadTaskRunnerHandle::Get()->PostDelayedTask(
tzik92b7a422017-04-11 15:00:44467 FROM_HERE, BindOnce(&IncrementInt, &task_count),
[email protected]59e69e742013-06-18 20:27:52468 TimeDelta::FromMilliseconds(30));
fdoray2df4a9e2016-07-18 23:47:16469 ThreadTaskRunnerHandle::Get()->PostDelayedTask(
tzik92b7a422017-04-11 15:00:44470 FROM_HERE, BindOnce(&GLibLoopRunner::Quit, runner),
[email protected]59e69e742013-06-18 20:27:52471 TimeDelta::FromMilliseconds(40));
[email protected]b44d5cc2009-06-15 10:30:44472
473 // Run a nested, straight GLib message loop.
Gabriel Charettebdbb58e72020-06-03 18:26:06474 {
Carlos Caballerob25fe8472020-07-17 10:27:17475 CurrentThread::ScopedNestableTaskAllower allow_nestable_tasks;
Gabriel Charettebdbb58e72020-06-03 18:26:06476 runner->RunGLib();
477 }
[email protected]b44d5cc2009-06-15 10:30:44478
479 ASSERT_EQ(3, task_count);
480 EXPECT_EQ(4, injector->processed_events());
Wezfda077b2018-06-07 21:18:11481 std::move(done).Run();
[email protected]b44d5cc2009-06-15 10:30:44482}
483
Wezfda077b2018-06-07 21:18:11484void TestGtkLoopInternal(EventInjector* injector, OnceClosure done) {
[email protected]b44d5cc2009-06-15 10:30:44485 scoped_refptr<GLibLoopRunner> runner = new GLibLoopRunner();
486
487 int task_count = 0;
488 // Add a couple of dummy events
[email protected]763839e2011-12-09 23:06:02489 injector->AddDummyEvent(0);
490 injector->AddDummyEvent(0);
[email protected]b44d5cc2009-06-15 10:30:44491 // Post a couple of dummy tasks
fdoray2df4a9e2016-07-18 23:47:16492 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
tzik92b7a422017-04-11 15:00:44493 BindOnce(&IncrementInt, &task_count));
fdoray2df4a9e2016-07-18 23:47:16494 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
tzik92b7a422017-04-11 15:00:44495 BindOnce(&IncrementInt, &task_count));
[email protected]b44d5cc2009-06-15 10:30:44496 // Delayed events
[email protected]763839e2011-12-09 23:06:02497 injector->AddDummyEvent(10);
498 injector->AddDummyEvent(10);
[email protected]b44d5cc2009-06-15 10:30:44499 // Delayed work
fdoray2df4a9e2016-07-18 23:47:16500 ThreadTaskRunnerHandle::Get()->PostDelayedTask(
tzik92b7a422017-04-11 15:00:44501 FROM_HERE, BindOnce(&IncrementInt, &task_count),
[email protected]59e69e742013-06-18 20:27:52502 TimeDelta::FromMilliseconds(30));
fdoray2df4a9e2016-07-18 23:47:16503 ThreadTaskRunnerHandle::Get()->PostDelayedTask(
tzik92b7a422017-04-11 15:00:44504 FROM_HERE, BindOnce(&GLibLoopRunner::Quit, runner),
[email protected]59e69e742013-06-18 20:27:52505 TimeDelta::FromMilliseconds(40));
[email protected]b44d5cc2009-06-15 10:30:44506
507 // Run a nested, straight Gtk message loop.
Gabriel Charettebdbb58e72020-06-03 18:26:06508 {
Carlos Caballerob25fe8472020-07-17 10:27:17509 CurrentThread::ScopedNestableTaskAllower allow_nestable_tasks;
Gabriel Charettebdbb58e72020-06-03 18:26:06510 runner->RunLoop();
511 }
[email protected]b44d5cc2009-06-15 10:30:44512
513 ASSERT_EQ(3, task_count);
514 EXPECT_EQ(4, injector->processed_events());
Wezfda077b2018-06-07 21:18:11515 std::move(done).Run();
[email protected]b44d5cc2009-06-15 10:30:44516}
517
518} // namespace
519
520TEST_F(MessagePumpGLibTest, TestGLibLoop) {
[email protected]8f5a7e492012-01-01 02:14:47521 // Tests that events and posted tasks are correctly executed if the message
[email protected]b44d5cc2009-06-15 10:30:44522 // loop is not run by MessageLoop::Run() but by a straight GLib loop.
523 // Note that in this case we don't make strong guarantees about niceness
524 // between events and posted tasks.
Wezfda077b2018-06-07 21:18:11525 RunLoop run_loop;
Carlos Caballerof5b6f91c2019-12-20 14:07:38526 ThreadTaskRunnerHandle::Get()->PostTask(
Wezfda077b2018-06-07 21:18:11527 FROM_HERE, BindOnce(&TestGLibLoopInternal, Unretained(injector()),
528 run_loop.QuitClosure()));
529 run_loop.Run();
[email protected]b44d5cc2009-06-15 10:30:44530}
531
532TEST_F(MessagePumpGLibTest, TestGtkLoop) {
[email protected]8f5a7e492012-01-01 02:14:47533 // Tests that events and posted tasks are correctly executed if the message
[email protected]b44d5cc2009-06-15 10:30:44534 // loop is not run by MessageLoop::Run() but by a straight Gtk loop.
535 // Note that in this case we don't make strong guarantees about niceness
536 // between events and posted tasks.
Wezfda077b2018-06-07 21:18:11537 RunLoop run_loop;
Carlos Caballerof5b6f91c2019-12-20 14:07:38538 ThreadTaskRunnerHandle::Get()->PostTask(
Wezfda077b2018-06-07 21:18:11539 FROM_HERE, BindOnce(&TestGtkLoopInternal, Unretained(injector()),
540 run_loop.QuitClosure()));
541 run_loop.Run();
[email protected]b44d5cc2009-06-15 10:30:44542}
[email protected]7ff48ca2013-02-06 16:56:19543
Nick Diego Yamane58914192019-08-08 17:12:48544// Tests for WatchFileDescriptor API
545class MessagePumpGLibFdWatchTest : public testing::Test {
546 protected:
547 MessagePumpGLibFdWatchTest()
548 : io_thread_("MessagePumpGLibFdWatchTestIOThread") {}
549 ~MessagePumpGLibFdWatchTest() override = default;
550
551 void SetUp() override {
552 Thread::Options options(MessagePumpType::IO, 0);
553 ASSERT_TRUE(io_thread_.StartWithOptions(options));
554 int ret = pipe(pipefds_);
555 ASSERT_EQ(0, ret);
556 }
557
558 void TearDown() override {
559 if (IGNORE_EINTR(close(pipefds_[0])) < 0)
560 PLOG(ERROR) << "close";
561 if (IGNORE_EINTR(close(pipefds_[1])) < 0)
562 PLOG(ERROR) << "close";
563 }
564
565 void WaitUntilIoThreadStarted() {
566 ASSERT_TRUE(io_thread_.WaitUntilThreadStarted());
567 }
568
569 scoped_refptr<SingleThreadTaskRunner> io_runner() const {
570 return io_thread_.task_runner();
571 }
572
573 void SimulateEvent(MessagePumpGlib* pump,
574 MessagePumpGlib::FdWatchController* controller) {
575 controller->poll_fd_->revents = G_IO_IN | G_IO_OUT;
576 pump->HandleFdWatchDispatch(controller);
577 }
578
579 int pipefds_[2];
580
581 private:
582 Thread io_thread_;
583};
584
585namespace {
586
587class BaseWatcher : public MessagePumpGlib::FdWatcher {
588 public:
589 explicit BaseWatcher(MessagePumpGlib::FdWatchController* controller)
590 : controller_(controller) {
591 DCHECK(controller_);
592 }
593 ~BaseWatcher() override = default;
594
595 // base:MessagePumpGlib::FdWatcher interface
596 void OnFileCanReadWithoutBlocking(int /* fd */) override { NOTREACHED(); }
597 void OnFileCanWriteWithoutBlocking(int /* fd */) override { NOTREACHED(); }
598
599 protected:
600 MessagePumpGlib::FdWatchController* controller_;
601};
602
603class DeleteWatcher : public BaseWatcher {
604 public:
605 explicit DeleteWatcher(
606 std::unique_ptr<MessagePumpGlib::FdWatchController> controller)
607 : BaseWatcher(controller.get()),
608 owned_controller_(std::move(controller)) {}
609
610 ~DeleteWatcher() override { DCHECK(!controller_); }
611
612 void OnFileCanWriteWithoutBlocking(int /* fd */) override {
613 DCHECK(owned_controller_);
614 owned_controller_.reset();
615 controller_ = nullptr;
616 }
617
618 private:
619 std::unique_ptr<MessagePumpGlib::FdWatchController> owned_controller_;
620};
621
622class StopWatcher : public BaseWatcher {
623 public:
624 explicit StopWatcher(MessagePumpGlib::FdWatchController* controller)
625 : BaseWatcher(controller) {}
626
627 ~StopWatcher() override = default;
628
629 void OnFileCanWriteWithoutBlocking(int /* fd */) override {
630 controller_->StopWatchingFileDescriptor();
631 }
632};
633
634void QuitMessageLoopAndStart(OnceClosure quit_closure) {
635 std::move(quit_closure).Run();
636
637 RunLoop runloop(RunLoop::Type::kNestableTasksAllowed);
638 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, runloop.QuitClosure());
639 runloop.Run();
640}
641
642class NestedPumpWatcher : public MessagePumpGlib::FdWatcher {
643 public:
644 NestedPumpWatcher() = default;
645 ~NestedPumpWatcher() override = default;
646
647 void OnFileCanReadWithoutBlocking(int /* fd */) override {
648 RunLoop runloop;
649 ThreadTaskRunnerHandle::Get()->PostTask(
650 FROM_HERE, BindOnce(&QuitMessageLoopAndStart, runloop.QuitClosure()));
651 runloop.Run();
652 }
653
654 void OnFileCanWriteWithoutBlocking(int /* fd */) override {}
655};
656
657class QuitWatcher : public BaseWatcher {
658 public:
659 QuitWatcher(MessagePumpGlib::FdWatchController* controller,
660 base::OnceClosure quit_closure)
661 : BaseWatcher(controller), quit_closure_(std::move(quit_closure)) {}
662
663 void OnFileCanReadWithoutBlocking(int /* fd */) override {
664 if (quit_closure_)
665 std::move(quit_closure_).Run();
666 }
667
668 private:
669 base::OnceClosure quit_closure_;
670};
671
672void WriteFDWrapper(const int fd,
673 const char* buf,
674 int size,
675 WaitableEvent* event) {
676 ASSERT_TRUE(WriteFileDescriptor(fd, buf, size));
677}
678
679} // namespace
680
681// Tests that MessagePumpGlib::FdWatcher::OnFileCanReadWithoutBlocking is not
682// called for a READ_WRITE event, when the controller is destroyed in
683// OnFileCanWriteWithoutBlocking callback.
684TEST_F(MessagePumpGLibFdWatchTest, DeleteWatcher) {
685 auto pump = std::make_unique<MessagePumpGlib>();
686 auto controller_ptr =
687 std::make_unique<MessagePumpGlib::FdWatchController>(FROM_HERE);
688 auto* controller = controller_ptr.get();
689
690 DeleteWatcher watcher(std::move(controller_ptr));
691 pump->WatchFileDescriptor(pipefds_[1], false,
692 MessagePumpGlib::WATCH_READ_WRITE, controller,
693 &watcher);
694
695 SimulateEvent(pump.get(), controller);
696}
697
698// Tests that MessagePumpGlib::FdWatcher::OnFileCanReadWithoutBlocking is not
699// called for a READ_WRITE event, when the watcher calls
700// StopWatchingFileDescriptor in OnFileCanWriteWithoutBlocking callback.
701TEST_F(MessagePumpGLibFdWatchTest, StopWatcher) {
702 std::unique_ptr<MessagePumpGlib> pump(new MessagePumpGlib);
703 MessagePumpGlib::FdWatchController controller(FROM_HERE);
704 StopWatcher watcher(&controller);
705 pump->WatchFileDescriptor(pipefds_[1], false,
706 MessagePumpGlib::WATCH_READ_WRITE, &controller,
707 &watcher);
708
709 SimulateEvent(pump.get(), &controller);
710}
711
712// Tests that FdWatcher works properly with nested loops.
713TEST_F(MessagePumpGLibFdWatchTest, NestedPumpWatcher) {
Carlos Caballerof5b6f91c2019-12-20 14:07:38714 test::SingleThreadTaskEnvironment task_environment(
715 test::SingleThreadTaskEnvironment::MainThreadType::UI);
Nick Diego Yamane58914192019-08-08 17:12:48716 std::unique_ptr<MessagePumpGlib> pump(new MessagePumpGlib);
717 MessagePumpGlib::FdWatchController controller(FROM_HERE);
718 NestedPumpWatcher watcher;
719 pump->WatchFileDescriptor(pipefds_[1], false, MessagePumpGlib::WATCH_READ,
720 &controller, &watcher);
721
722 SimulateEvent(pump.get(), &controller);
723}
724
725// Tests that MessagePumpGlib quits immediately when it is quit from
726// libevent's event_base_loop().
727TEST_F(MessagePumpGLibFdWatchTest, QuitWatcher) {
Carlos Caballerof5b6f91c2019-12-20 14:07:38728 MessagePumpGlib* pump = new MessagePumpGlib();
729 SingleThreadTaskExecutor executor(WrapUnique(pump));
Nick Diego Yamane58914192019-08-08 17:12:48730 RunLoop run_loop;
731 MessagePumpGlib::FdWatchController controller(FROM_HERE);
732 QuitWatcher delegate(&controller, run_loop.QuitClosure());
733 WaitableEvent event;
734 auto watcher = std::make_unique<WaitableEventWatcher>();
735
736 pump->WatchFileDescriptor(pipefds_[0], false, MessagePumpGlib::WATCH_READ,
737 &controller, &delegate);
738
739 // Make the IO thread wait for |event| before writing to pipefds[1].
740 const char buf = 0;
741 WaitableEventWatcher::EventCallback write_fd_task =
742 BindOnce(&WriteFDWrapper, pipefds_[1], &buf, 1);
743 io_runner()->PostTask(
744 FROM_HERE, BindOnce(IgnoreResult(&WaitableEventWatcher::StartWatching),
745 Unretained(watcher.get()), &event,
746 std::move(write_fd_task), io_runner()));
747
Carlos Caballerob25fe8472020-07-17 10:27:17748 // Queue |event| to signal on |CurrentUIThread::Get()|.
Nick Diego Yamane58914192019-08-08 17:12:48749 ThreadTaskRunnerHandle::Get()->PostTask(
750 FROM_HERE, BindOnce(&WaitableEvent::Signal, Unretained(&event)));
751
752 // Now run the MessageLoop.
753 run_loop.Run();
754
755 // StartWatching can move |watcher| to IO thread. Release on IO thread.
756 io_runner()->PostTask(FROM_HERE, BindOnce(&WaitableEventWatcher::StopWatching,
757 Owned(std::move(watcher))));
758}
759
[email protected]7ff48ca2013-02-06 16:56:19760} // namespace base