blob: d4c72b5907254fc3bf1b7a3e3a9ba3150b175db9 [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"
[email protected]763839e2011-12-09 23:06:0214#include "base/callback.h"
danakjdb9ae7942020-11-11 16:01:3515#include "base/callback_helpers.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
Peter Boström7319bbd2021-09-15 22:59:3849 EventInjector(const EventInjector&) = delete;
50 EventInjector& operator=(const EventInjector&) = delete;
51
[email protected]b44d5cc2009-06-15 10:30:4452 ~EventInjector() {
53 g_source_destroy(source_);
54 g_source_unref(source_);
55 }
56
57 int HandlePrepare() {
58 // If the queue is empty, block.
59 if (events_.empty())
60 return -1;
[email protected]59e69e742013-06-18 20:27:5261 TimeDelta delta = events_[0].time - Time::NowFromSystemTime();
[email protected]b44d5cc2009-06-15 10:30:4462 return std::max(0, static_cast<int>(ceil(delta.InMillisecondsF())));
63 }
64
65 bool HandleCheck() {
66 if (events_.empty())
67 return false;
[email protected]59e69e742013-06-18 20:27:5268 return events_[0].time <= Time::NowFromSystemTime();
[email protected]b44d5cc2009-06-15 10:30:4469 }
70
71 void HandleDispatch() {
72 if (events_.empty())
73 return;
tzika8cc2202017-04-18 07:01:1574 Event event = std::move(events_[0]);
[email protected]b44d5cc2009-06-15 10:30:4475 events_.erase(events_.begin());
76 ++processed_events_;
[email protected]8f5a7e492012-01-01 02:14:4777 if (!event.callback.is_null())
tzika8cc2202017-04-18 07:01:1578 std::move(event.callback).Run();
[email protected]8f5a7e492012-01-01 02:14:4779 else if (!event.task.is_null())
tzika8cc2202017-04-18 07:01:1580 std::move(event.task).Run();
[email protected]b44d5cc2009-06-15 10:30:4481 }
82
[email protected]763839e2011-12-09 23:06:0283 // Adds an event to the queue. When "handled", executes |callback|.
[email protected]b44d5cc2009-06-15 10:30:4484 // delay_ms is relative to the last event if any, or to Now() otherwise.
tzika8cc2202017-04-18 07:01:1585 void AddEvent(int delay_ms, OnceClosure callback) {
86 AddEventHelper(delay_ms, std::move(callback), OnceClosure());
[email protected]763839e2011-12-09 23:06:0287 }
88
89 void AddDummyEvent(int delay_ms) {
tzika8cc2202017-04-18 07:01:1590 AddEventHelper(delay_ms, OnceClosure(), OnceClosure());
[email protected]763839e2011-12-09 23:06:0291 }
92
tzika8cc2202017-04-18 07:01:1593 void AddEventAsTask(int delay_ms, OnceClosure task) {
94 AddEventHelper(delay_ms, OnceClosure(), std::move(task));
[email protected]b44d5cc2009-06-15 10:30:4495 }
96
97 void Reset() {
98 processed_events_ = 0;
99 events_.clear();
100 }
101
102 int processed_events() const { return processed_events_; }
103
104 private:
105 struct Event {
[email protected]59e69e742013-06-18 20:27:52106 Time time;
tzika8cc2202017-04-18 07:01:15107 OnceClosure callback;
108 OnceClosure task;
[email protected]b44d5cc2009-06-15 10:30:44109 };
110
111 struct Source : public GSource {
112 EventInjector* injector;
113 };
114
tzika8cc2202017-04-18 07:01:15115 void AddEventHelper(int delay_ms, OnceClosure callback, OnceClosure task) {
[email protected]59e69e742013-06-18 20:27:52116 Time last_time;
[email protected]8f5a7e492012-01-01 02:14:47117 if (!events_.empty())
[email protected]763839e2011-12-09 23:06:02118 last_time = (events_.end()-1)->time;
[email protected]8f5a7e492012-01-01 02:14:47119 else
[email protected]59e69e742013-06-18 20:27:52120 last_time = Time::NowFromSystemTime();
[email protected]8f5a7e492012-01-01 02:14:47121
[email protected]59e69e742013-06-18 20:27:52122 Time future = last_time + TimeDelta::FromMilliseconds(delay_ms);
tzika8cc2202017-04-18 07:01:15123 EventInjector::Event event = {future, std::move(callback), std::move(task)};
124 events_.push_back(std::move(event));
[email protected]763839e2011-12-09 23:06:02125 }
126
[email protected]b44d5cc2009-06-15 10:30:44127 static gboolean Prepare(GSource* source, gint* timeout_ms) {
128 *timeout_ms = static_cast<Source*>(source)->injector->HandlePrepare();
129 return FALSE;
130 }
131
132 static gboolean Check(GSource* source) {
133 return static_cast<Source*>(source)->injector->HandleCheck();
134 }
135
136 static gboolean Dispatch(GSource* source,
137 GSourceFunc unused_func,
138 gpointer unused_data) {
139 static_cast<Source*>(source)->injector->HandleDispatch();
140 return TRUE;
141 }
142
143 Source* source_;
144 std::vector<Event> events_;
145 int processed_events_;
146 static GSourceFuncs SourceFuncs;
[email protected]b44d5cc2009-06-15 10:30:44147};
148
Ivan Kotenkova16212a52017-11-08 12:37:33149GSourceFuncs EventInjector::SourceFuncs = {EventInjector::Prepare,
150 EventInjector::Check,
151 EventInjector::Dispatch, nullptr};
[email protected]b44d5cc2009-06-15 10:30:44152
[email protected]b44d5cc2009-06-15 10:30:44153void IncrementInt(int *value) {
154 ++*value;
155}
156
157// Checks how many events have been processed by the injector.
158void ExpectProcessedEvents(EventInjector* injector, int count) {
159 EXPECT_EQ(injector->processed_events(), count);
160}
161
[email protected]b44d5cc2009-06-15 10:30:44162// Posts a task on the current message loop.
Brett Wilson8e88b312017-09-12 05:22:16163void PostMessageLoopTask(const Location& from_here, OnceClosure task) {
tzika8cc2202017-04-18 07:01:15164 ThreadTaskRunnerHandle::Get()->PostTask(from_here, std::move(task));
[email protected]b44d5cc2009-06-15 10:30:44165}
166
167// Test fixture.
168class MessagePumpGLibTest : public testing::Test {
169 public:
Carlos Caballerof5b6f91c2019-12-20 14:07:38170 MessagePumpGLibTest() = default;
Peter Boström75cd3c02021-09-28 15:23:18171
172 MessagePumpGLibTest(const MessagePumpGLibTest&) = delete;
173 MessagePumpGLibTest& operator=(const MessagePumpGLibTest&) = delete;
174
Carlos Caballerof5b6f91c2019-12-20 14:07:38175 EventInjector* injector() { return &injector_; }
[email protected]b44d5cc2009-06-15 10:30:44176
177 private:
Carlos Caballerof5b6f91c2019-12-20 14:07:38178 test::SingleThreadTaskEnvironment task_environment_{
179 test::SingleThreadTaskEnvironment::MainThreadType::UI};
180 EventInjector injector_;
[email protected]b44d5cc2009-06-15 10:30:44181};
182
183} // namespace
184
[email protected]b44d5cc2009-06-15 10:30:44185TEST_F(MessagePumpGLibTest, TestQuit) {
186 // Checks that Quit works and that the basic infrastructure is working.
187
188 // Quit from a task
[email protected]7ff48ca2013-02-06 16:56:19189 RunLoop().RunUntilIdle();
[email protected]b44d5cc2009-06-15 10:30:44190 EXPECT_EQ(0, injector()->processed_events());
191
192 injector()->Reset();
193 // Quit from an event
Wezfda077b2018-06-07 21:18:11194 RunLoop run_loop;
195 injector()->AddEvent(0, run_loop.QuitClosure());
196 run_loop.Run();
[email protected]b44d5cc2009-06-15 10:30:44197 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.
Peter Kasting341e1fb2018-02-24 00:03:01208 injector()->AddEventAsTask(0, DoNothing());
tzika8cc2202017-04-18 07:01:15209 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));
Peter Kasting341e1fb2018-02-24 00:03:01214 injector()->AddEventAsTask(0, DoNothing());
Wezfda077b2018-06-07 21:18:11215 {
216 RunLoop run_loop;
217 injector()->AddEvent(0, run_loop.QuitClosure());
218 run_loop.Run();
219 }
[email protected]b44d5cc2009-06-15 10:30:44220 EXPECT_EQ(4, injector()->processed_events());
221
222 injector()->Reset();
Peter Kasting341e1fb2018-02-24 00:03:01223 injector()->AddEventAsTask(0, DoNothing());
tzika8cc2202017-04-18 07:01:15224 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));
Peter Kasting341e1fb2018-02-24 00:03:01228 injector()->AddEventAsTask(10, DoNothing());
Wezfda077b2018-06-07 21:18:11229 {
230 RunLoop run_loop;
231 injector()->AddEvent(0, run_loop.QuitClosure());
232 run_loop.Run();
233 }
[email protected]b44d5cc2009-06-15 10:30:44234 EXPECT_EQ(4, injector()->processed_events());
235}
236
237TEST_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) {
Carlos Caballerof5b6f91c2019-12-20 14:07:38242 ThreadTaskRunnerHandle::Get()->PostTask(
243 FROM_HERE, BindOnce(&IncrementInt, &task_count));
[email protected]b44d5cc2009-06-15 10:30:44244 }
245 // After all the previous tasks have executed, enqueue an event that will
246 // quit.
Wezfda077b2018-06-07 21:18:11247 {
248 RunLoop run_loop;
Carlos Caballerof5b6f91c2019-12-20 14:07:38249 ThreadTaskRunnerHandle::Get()->PostTask(
Wezfda077b2018-06-07 21:18:11250 FROM_HERE, BindOnce(&EventInjector::AddEvent, Unretained(injector()), 0,
251 run_loop.QuitClosure()));
252 run_loop.Run();
253 }
[email protected]b44d5cc2009-06-15 10:30:44254 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) {
Carlos Caballerof5b6f91c2019-12-20 14:07:38261 ThreadTaskRunnerHandle::Get()->PostDelayedTask(
262 FROM_HERE, BindOnce(&IncrementInt, &task_count),
263 TimeDelta::FromMilliseconds(10 * i));
[email protected]b44d5cc2009-06-15 10:30:44264 }
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.
Wezfda077b2018-06-07 21:18:11269 {
270 RunLoop run_loop;
Carlos Caballerof5b6f91c2019-12-20 14:07:38271 ThreadTaskRunnerHandle::Get()->PostDelayedTask(
Wezfda077b2018-06-07 21:18:11272 FROM_HERE,
273 BindOnce(&EventInjector::AddEvent, Unretained(injector()), 0,
274 run_loop.QuitClosure()),
275 TimeDelta::FromMilliseconds(150));
276 run_loop.Run();
277 }
[email protected]b44d5cc2009-06-15 10:30:44278 ASSERT_EQ(10, task_count);
279 EXPECT_EQ(1, injector()->processed_events());
280}
281
282TEST_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) {
[email protected]763839e2011-12-09 23:06:02286 injector()->AddDummyEvent(0);
[email protected]b44d5cc2009-06-15 10:30:44287 }
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).
tzika8cc2202017-04-18 07:01:15291 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));
[email protected]b44d5cc2009-06-15 10:30:44296
297 // And then quit (relies on the condition tested by TestEventTaskInterleave).
Wezfda077b2018-06-07 21:18:11298 RunLoop run_loop;
299 injector()->AddEvent(10, run_loop.QuitClosure());
300 run_loop.Run();
[email protected]b44d5cc2009-06-15 10:30:44301
302 EXPECT_EQ(12, injector()->processed_events());
303}
304
305namespace {
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.
[email protected]59e69e742013-06-18 20:27:52310class ConcurrentHelper : public RefCounted<ConcurrentHelper> {
[email protected]b44d5cc2009-06-15 10:30:44311 public:
Wezfda077b2018-06-07 21:18:11312 ConcurrentHelper(EventInjector* injector, OnceClosure done_closure)
[email protected]b44d5cc2009-06-15 10:30:44313 : injector_(injector),
Wezfda077b2018-06-07 21:18:11314 done_closure_(std::move(done_closure)),
[email protected]b44d5cc2009-06-15 10:30:44315 event_count_(kStartingEventCount),
Wezfda077b2018-06-07 21:18:11316 task_count_(kStartingTaskCount) {}
[email protected]b44d5cc2009-06-15 10:30:44317
318 void FromTask() {
319 if (task_count_ > 0) {
320 --task_count_;
321 }
322 if (task_count_ == 0 && event_count_ == 0) {
Wezfda077b2018-06-07 21:18:11323 std::move(done_closure_).Run();
[email protected]b44d5cc2009-06-15 10:30:44324 } else {
fdoray2df4a9e2016-07-18 23:47:16325 ThreadTaskRunnerHandle::Get()->PostTask(
tzik92b7a422017-04-11 15:00:44326 FROM_HERE, BindOnce(&ConcurrentHelper::FromTask, this));
[email protected]b44d5cc2009-06-15 10:30:44327 }
328 }
329
330 void FromEvent() {
331 if (event_count_ > 0) {
332 --event_count_;
333 }
334 if (task_count_ == 0 && event_count_ == 0) {
Wezfda077b2018-06-07 21:18:11335 std::move(done_closure_).Run();
[email protected]b44d5cc2009-06-15 10:30:44336 } else {
tzika8cc2202017-04-18 07:01:15337 injector_->AddEventAsTask(0,
338 BindOnce(&ConcurrentHelper::FromEvent, this));
[email protected]b44d5cc2009-06-15 10:30:44339 }
340 }
341
342 int event_count() const { return event_count_; }
343 int task_count() const { return task_count_; }
344
345 private:
[email protected]59e69e742013-06-18 20:27:52346 friend class RefCounted<ConcurrentHelper>;
[email protected]877d55d2009-11-05 21:53:08347
348 ~ConcurrentHelper() {}
349
[email protected]b44d5cc2009-06-15 10:30:44350 static const int kStartingEventCount = 20;
351 static const int kStartingTaskCount = 20;
352
353 EventInjector* injector_;
Wezfda077b2018-06-07 21:18:11354 OnceClosure done_closure_;
[email protected]b44d5cc2009-06-15 10:30:44355 int event_count_;
356 int task_count_;
357};
358
359} // namespace
360
361TEST_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
[email protected]91cae2592013-01-10 14:56:17366 // to 0, and MessageLoop::QuitWhenIdle() will never be called.
Wezfda077b2018-06-07 21:18:11367 RunLoop run_loop;
368 scoped_refptr<ConcurrentHelper> helper =
369 new ConcurrentHelper(injector(), run_loop.QuitClosure());
[email protected]b44d5cc2009-06-15 10:30:44370
371 // Add 2 events to the queue to make sure it is always full (when we remove
372 // the event before processing it).
tzika8cc2202017-04-18 07:01:15373 injector()->AddEventAsTask(0, BindOnce(&ConcurrentHelper::FromEvent, helper));
374 injector()->AddEventAsTask(0, BindOnce(&ConcurrentHelper::FromEvent, helper));
[email protected]b44d5cc2009-06-15 10:30:44375
376 // Similarly post 2 tasks.
Carlos Caballerof5b6f91c2019-12-20 14:07:38377 ThreadTaskRunnerHandle::Get()->PostTask(
tzik92b7a422017-04-11 15:00:44378 FROM_HERE, BindOnce(&ConcurrentHelper::FromTask, helper));
Carlos Caballerof5b6f91c2019-12-20 14:07:38379 ThreadTaskRunnerHandle::Get()->PostTask(
tzik92b7a422017-04-11 15:00:44380 FROM_HERE, BindOnce(&ConcurrentHelper::FromTask, helper));
[email protected]b44d5cc2009-06-15 10:30:44381
Wezfda077b2018-06-07 21:18:11382 run_loop.Run();
[email protected]b44d5cc2009-06-15 10:30:44383 EXPECT_EQ(0, helper->event_count());
384 EXPECT_EQ(0, helper->task_count());
385}
386
387namespace {
388
Wezfda077b2018-06-07 21:18:11389void AddEventsAndDrainGLib(EventInjector* injector, OnceClosure on_drained) {
[email protected]b44d5cc2009-06-15 10:30:44390 // Add a couple of dummy events
[email protected]763839e2011-12-09 23:06:02391 injector->AddDummyEvent(0);
392 injector->AddDummyEvent(0);
[email protected]b44d5cc2009-06-15 10:30:44393 // Then add an event that will quit the main loop.
Wezfda077b2018-06-07 21:18:11394 injector->AddEvent(0, std::move(on_drained));
[email protected]b44d5cc2009-06-15 10:30:44395
396 // Post a couple of dummy tasks
Peter Kasting341e1fb2018-02-24 00:03:01397 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, DoNothing());
398 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, DoNothing());
[email protected]b44d5cc2009-06-15 10:30:44399
400 // Drain the events
Ivan Kotenkova16212a52017-11-08 12:37:33401 while (g_main_context_pending(nullptr)) {
402 g_main_context_iteration(nullptr, FALSE);
[email protected]b44d5cc2009-06-15 10:30:44403 }
404}
405
406} // namespace
407
408TEST_F(MessagePumpGLibTest, TestDrainingGLib) {
409 // Tests that draining events using GLib works.
Wezfda077b2018-06-07 21:18:11410 RunLoop run_loop;
Carlos Caballerof5b6f91c2019-12-20 14:07:38411 ThreadTaskRunnerHandle::Get()->PostTask(
Wezfda077b2018-06-07 21:18:11412 FROM_HERE, BindOnce(&AddEventsAndDrainGLib, Unretained(injector()),
413 run_loop.QuitClosure()));
414 run_loop.Run();
[email protected]b44d5cc2009-06-15 10:30:44415
416 EXPECT_EQ(3, injector()->processed_events());
417}
418
[email protected]b44d5cc2009-06-15 10:30:44419namespace {
420
421// Helper class that lets us run the GLib message loop.
[email protected]59e69e742013-06-18 20:27:52422class GLibLoopRunner : public RefCounted<GLibLoopRunner> {
[email protected]b44d5cc2009-06-15 10:30:44423 public:
424 GLibLoopRunner() : quit_(false) { }
425
426 void RunGLib() {
427 while (!quit_) {
Ivan Kotenkova16212a52017-11-08 12:37:33428 g_main_context_iteration(nullptr, TRUE);
[email protected]b44d5cc2009-06-15 10:30:44429 }
430 }
431
[email protected]258dca42011-09-21 00:17:19432 void RunLoop() {
[email protected]258dca42011-09-21 00:17:19433 while (!quit_) {
Ivan Kotenkova16212a52017-11-08 12:37:33434 g_main_context_iteration(nullptr, TRUE);
[email protected]258dca42011-09-21 00:17:19435 }
[email protected]b44d5cc2009-06-15 10:30:44436 }
437
438 void Quit() {
439 quit_ = true;
440 }
441
442 void Reset() {
443 quit_ = false;
444 }
445
446 private:
[email protected]59e69e742013-06-18 20:27:52447 friend class RefCounted<GLibLoopRunner>;
[email protected]877d55d2009-11-05 21:53:08448
449 ~GLibLoopRunner() {}
450
[email protected]b44d5cc2009-06-15 10:30:44451 bool quit_;
452};
453
Wezfda077b2018-06-07 21:18:11454void TestGLibLoopInternal(EventInjector* injector, OnceClosure done) {
[email protected]b44d5cc2009-06-15 10:30:44455 scoped_refptr<GLibLoopRunner> runner = new GLibLoopRunner();
456
457 int task_count = 0;
458 // Add a couple of dummy events
[email protected]763839e2011-12-09 23:06:02459 injector->AddDummyEvent(0);
460 injector->AddDummyEvent(0);
[email protected]b44d5cc2009-06-15 10:30:44461 // Post a couple of dummy tasks
fdoray2df4a9e2016-07-18 23:47:16462 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
tzik92b7a422017-04-11 15:00:44463 BindOnce(&IncrementInt, &task_count));
fdoray2df4a9e2016-07-18 23:47:16464 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
tzik92b7a422017-04-11 15:00:44465 BindOnce(&IncrementInt, &task_count));
[email protected]b44d5cc2009-06-15 10:30:44466 // Delayed events
[email protected]763839e2011-12-09 23:06:02467 injector->AddDummyEvent(10);
468 injector->AddDummyEvent(10);
[email protected]b44d5cc2009-06-15 10:30:44469 // Delayed work
fdoray2df4a9e2016-07-18 23:47:16470 ThreadTaskRunnerHandle::Get()->PostDelayedTask(
tzik92b7a422017-04-11 15:00:44471 FROM_HERE, BindOnce(&IncrementInt, &task_count),
[email protected]59e69e742013-06-18 20:27:52472 TimeDelta::FromMilliseconds(30));
fdoray2df4a9e2016-07-18 23:47:16473 ThreadTaskRunnerHandle::Get()->PostDelayedTask(
tzik92b7a422017-04-11 15:00:44474 FROM_HERE, BindOnce(&GLibLoopRunner::Quit, runner),
[email protected]59e69e742013-06-18 20:27:52475 TimeDelta::FromMilliseconds(40));
[email protected]b44d5cc2009-06-15 10:30:44476
477 // Run a nested, straight GLib message loop.
Gabriel Charettebdbb58e72020-06-03 18:26:06478 {
Carlos Caballerob25fe8472020-07-17 10:27:17479 CurrentThread::ScopedNestableTaskAllower allow_nestable_tasks;
Gabriel Charettebdbb58e72020-06-03 18:26:06480 runner->RunGLib();
481 }
[email protected]b44d5cc2009-06-15 10:30:44482
483 ASSERT_EQ(3, task_count);
484 EXPECT_EQ(4, injector->processed_events());
Wezfda077b2018-06-07 21:18:11485 std::move(done).Run();
[email protected]b44d5cc2009-06-15 10:30:44486}
487
Wezfda077b2018-06-07 21:18:11488void TestGtkLoopInternal(EventInjector* injector, OnceClosure done) {
[email protected]b44d5cc2009-06-15 10:30:44489 scoped_refptr<GLibLoopRunner> runner = new GLibLoopRunner();
490
491 int task_count = 0;
492 // Add a couple of dummy events
[email protected]763839e2011-12-09 23:06:02493 injector->AddDummyEvent(0);
494 injector->AddDummyEvent(0);
[email protected]b44d5cc2009-06-15 10:30:44495 // Post a couple of dummy tasks
fdoray2df4a9e2016-07-18 23:47:16496 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
tzik92b7a422017-04-11 15:00:44497 BindOnce(&IncrementInt, &task_count));
fdoray2df4a9e2016-07-18 23:47:16498 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
tzik92b7a422017-04-11 15:00:44499 BindOnce(&IncrementInt, &task_count));
[email protected]b44d5cc2009-06-15 10:30:44500 // Delayed events
[email protected]763839e2011-12-09 23:06:02501 injector->AddDummyEvent(10);
502 injector->AddDummyEvent(10);
[email protected]b44d5cc2009-06-15 10:30:44503 // Delayed work
fdoray2df4a9e2016-07-18 23:47:16504 ThreadTaskRunnerHandle::Get()->PostDelayedTask(
tzik92b7a422017-04-11 15:00:44505 FROM_HERE, BindOnce(&IncrementInt, &task_count),
[email protected]59e69e742013-06-18 20:27:52506 TimeDelta::FromMilliseconds(30));
fdoray2df4a9e2016-07-18 23:47:16507 ThreadTaskRunnerHandle::Get()->PostDelayedTask(
tzik92b7a422017-04-11 15:00:44508 FROM_HERE, BindOnce(&GLibLoopRunner::Quit, runner),
[email protected]59e69e742013-06-18 20:27:52509 TimeDelta::FromMilliseconds(40));
[email protected]b44d5cc2009-06-15 10:30:44510
511 // Run a nested, straight Gtk message loop.
Gabriel Charettebdbb58e72020-06-03 18:26:06512 {
Carlos Caballerob25fe8472020-07-17 10:27:17513 CurrentThread::ScopedNestableTaskAllower allow_nestable_tasks;
Gabriel Charettebdbb58e72020-06-03 18:26:06514 runner->RunLoop();
515 }
[email protected]b44d5cc2009-06-15 10:30:44516
517 ASSERT_EQ(3, task_count);
518 EXPECT_EQ(4, injector->processed_events());
Wezfda077b2018-06-07 21:18:11519 std::move(done).Run();
[email protected]b44d5cc2009-06-15 10:30:44520}
521
522} // namespace
523
524TEST_F(MessagePumpGLibTest, TestGLibLoop) {
[email protected]8f5a7e492012-01-01 02:14:47525 // Tests that events and posted tasks are correctly executed if the message
[email protected]b44d5cc2009-06-15 10:30:44526 // loop is not run by MessageLoop::Run() but by a straight GLib loop.
527 // Note that in this case we don't make strong guarantees about niceness
528 // between events and posted tasks.
Wezfda077b2018-06-07 21:18:11529 RunLoop run_loop;
Carlos Caballerof5b6f91c2019-12-20 14:07:38530 ThreadTaskRunnerHandle::Get()->PostTask(
Wezfda077b2018-06-07 21:18:11531 FROM_HERE, BindOnce(&TestGLibLoopInternal, Unretained(injector()),
532 run_loop.QuitClosure()));
533 run_loop.Run();
[email protected]b44d5cc2009-06-15 10:30:44534}
535
536TEST_F(MessagePumpGLibTest, TestGtkLoop) {
[email protected]8f5a7e492012-01-01 02:14:47537 // Tests that events and posted tasks are correctly executed if the message
[email protected]b44d5cc2009-06-15 10:30:44538 // loop is not run by MessageLoop::Run() but by a straight Gtk loop.
539 // Note that in this case we don't make strong guarantees about niceness
540 // between events and posted tasks.
Wezfda077b2018-06-07 21:18:11541 RunLoop run_loop;
Carlos Caballerof5b6f91c2019-12-20 14:07:38542 ThreadTaskRunnerHandle::Get()->PostTask(
Wezfda077b2018-06-07 21:18:11543 FROM_HERE, BindOnce(&TestGtkLoopInternal, Unretained(injector()),
544 run_loop.QuitClosure()));
545 run_loop.Run();
[email protected]b44d5cc2009-06-15 10:30:44546}
[email protected]7ff48ca2013-02-06 16:56:19547
Nick Diego Yamane58914192019-08-08 17:12:48548// Tests for WatchFileDescriptor API
549class MessagePumpGLibFdWatchTest : public testing::Test {
550 protected:
551 MessagePumpGLibFdWatchTest()
552 : io_thread_("MessagePumpGLibFdWatchTestIOThread") {}
553 ~MessagePumpGLibFdWatchTest() override = default;
554
555 void SetUp() override {
556 Thread::Options options(MessagePumpType::IO, 0);
Olivier Lid68d0792021-05-17 20:51:41557 ASSERT_TRUE(io_thread_.StartWithOptions(std::move(options)));
Nick Diego Yamane58914192019-08-08 17:12:48558 int ret = pipe(pipefds_);
559 ASSERT_EQ(0, ret);
560 }
561
562 void TearDown() override {
563 if (IGNORE_EINTR(close(pipefds_[0])) < 0)
564 PLOG(ERROR) << "close";
565 if (IGNORE_EINTR(close(pipefds_[1])) < 0)
566 PLOG(ERROR) << "close";
567 }
568
569 void WaitUntilIoThreadStarted() {
570 ASSERT_TRUE(io_thread_.WaitUntilThreadStarted());
571 }
572
573 scoped_refptr<SingleThreadTaskRunner> io_runner() const {
574 return io_thread_.task_runner();
575 }
576
577 void SimulateEvent(MessagePumpGlib* pump,
578 MessagePumpGlib::FdWatchController* controller) {
579 controller->poll_fd_->revents = G_IO_IN | G_IO_OUT;
580 pump->HandleFdWatchDispatch(controller);
581 }
582
583 int pipefds_[2];
584
585 private:
586 Thread io_thread_;
587};
588
589namespace {
590
591class BaseWatcher : public MessagePumpGlib::FdWatcher {
592 public:
593 explicit BaseWatcher(MessagePumpGlib::FdWatchController* controller)
594 : controller_(controller) {
595 DCHECK(controller_);
596 }
597 ~BaseWatcher() override = default;
598
599 // base:MessagePumpGlib::FdWatcher interface
600 void OnFileCanReadWithoutBlocking(int /* fd */) override { NOTREACHED(); }
601 void OnFileCanWriteWithoutBlocking(int /* fd */) override { NOTREACHED(); }
602
603 protected:
604 MessagePumpGlib::FdWatchController* controller_;
605};
606
607class DeleteWatcher : public BaseWatcher {
608 public:
609 explicit DeleteWatcher(
610 std::unique_ptr<MessagePumpGlib::FdWatchController> controller)
611 : BaseWatcher(controller.get()),
612 owned_controller_(std::move(controller)) {}
613
614 ~DeleteWatcher() override { DCHECK(!controller_); }
615
616 void OnFileCanWriteWithoutBlocking(int /* fd */) override {
617 DCHECK(owned_controller_);
618 owned_controller_.reset();
619 controller_ = nullptr;
620 }
621
622 private:
623 std::unique_ptr<MessagePumpGlib::FdWatchController> owned_controller_;
624};
625
626class StopWatcher : public BaseWatcher {
627 public:
628 explicit StopWatcher(MessagePumpGlib::FdWatchController* controller)
629 : BaseWatcher(controller) {}
630
631 ~StopWatcher() override = default;
632
633 void OnFileCanWriteWithoutBlocking(int /* fd */) override {
634 controller_->StopWatchingFileDescriptor();
635 }
636};
637
638void QuitMessageLoopAndStart(OnceClosure quit_closure) {
639 std::move(quit_closure).Run();
640
641 RunLoop runloop(RunLoop::Type::kNestableTasksAllowed);
642 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, runloop.QuitClosure());
643 runloop.Run();
644}
645
646class NestedPumpWatcher : public MessagePumpGlib::FdWatcher {
647 public:
648 NestedPumpWatcher() = default;
649 ~NestedPumpWatcher() override = default;
650
651 void OnFileCanReadWithoutBlocking(int /* fd */) override {
652 RunLoop runloop;
653 ThreadTaskRunnerHandle::Get()->PostTask(
654 FROM_HERE, BindOnce(&QuitMessageLoopAndStart, runloop.QuitClosure()));
655 runloop.Run();
656 }
657
658 void OnFileCanWriteWithoutBlocking(int /* fd */) override {}
659};
660
661class QuitWatcher : public BaseWatcher {
662 public:
663 QuitWatcher(MessagePumpGlib::FdWatchController* controller,
664 base::OnceClosure quit_closure)
665 : BaseWatcher(controller), quit_closure_(std::move(quit_closure)) {}
666
667 void OnFileCanReadWithoutBlocking(int /* fd */) override {
668 if (quit_closure_)
669 std::move(quit_closure_).Run();
670 }
671
672 private:
673 base::OnceClosure quit_closure_;
674};
675
676void WriteFDWrapper(const int fd,
677 const char* buf,
678 int size,
679 WaitableEvent* event) {
Lei Zhangc9e8a162021-05-14 09:33:52680 ASSERT_TRUE(WriteFileDescriptor(fd, StringPiece(buf, size)));
Nick Diego Yamane58914192019-08-08 17:12:48681}
682
683} // namespace
684
685// Tests that MessagePumpGlib::FdWatcher::OnFileCanReadWithoutBlocking is not
686// called for a READ_WRITE event, when the controller is destroyed in
687// OnFileCanWriteWithoutBlocking callback.
688TEST_F(MessagePumpGLibFdWatchTest, DeleteWatcher) {
689 auto pump = std::make_unique<MessagePumpGlib>();
690 auto controller_ptr =
691 std::make_unique<MessagePumpGlib::FdWatchController>(FROM_HERE);
692 auto* controller = controller_ptr.get();
693
694 DeleteWatcher watcher(std::move(controller_ptr));
695 pump->WatchFileDescriptor(pipefds_[1], false,
696 MessagePumpGlib::WATCH_READ_WRITE, controller,
697 &watcher);
698
699 SimulateEvent(pump.get(), controller);
700}
701
702// Tests that MessagePumpGlib::FdWatcher::OnFileCanReadWithoutBlocking is not
703// called for a READ_WRITE event, when the watcher calls
704// StopWatchingFileDescriptor in OnFileCanWriteWithoutBlocking callback.
705TEST_F(MessagePumpGLibFdWatchTest, StopWatcher) {
706 std::unique_ptr<MessagePumpGlib> pump(new MessagePumpGlib);
707 MessagePumpGlib::FdWatchController controller(FROM_HERE);
708 StopWatcher watcher(&controller);
709 pump->WatchFileDescriptor(pipefds_[1], false,
710 MessagePumpGlib::WATCH_READ_WRITE, &controller,
711 &watcher);
712
713 SimulateEvent(pump.get(), &controller);
714}
715
716// Tests that FdWatcher works properly with nested loops.
717TEST_F(MessagePumpGLibFdWatchTest, NestedPumpWatcher) {
Carlos Caballerof5b6f91c2019-12-20 14:07:38718 test::SingleThreadTaskEnvironment task_environment(
719 test::SingleThreadTaskEnvironment::MainThreadType::UI);
Nick Diego Yamane58914192019-08-08 17:12:48720 std::unique_ptr<MessagePumpGlib> pump(new MessagePumpGlib);
721 MessagePumpGlib::FdWatchController controller(FROM_HERE);
722 NestedPumpWatcher watcher;
723 pump->WatchFileDescriptor(pipefds_[1], false, MessagePumpGlib::WATCH_READ,
724 &controller, &watcher);
725
726 SimulateEvent(pump.get(), &controller);
727}
728
729// Tests that MessagePumpGlib quits immediately when it is quit from
730// libevent's event_base_loop().
731TEST_F(MessagePumpGLibFdWatchTest, QuitWatcher) {
Carlos Caballerof5b6f91c2019-12-20 14:07:38732 MessagePumpGlib* pump = new MessagePumpGlib();
733 SingleThreadTaskExecutor executor(WrapUnique(pump));
Nick Diego Yamane58914192019-08-08 17:12:48734 RunLoop run_loop;
735 MessagePumpGlib::FdWatchController controller(FROM_HERE);
736 QuitWatcher delegate(&controller, run_loop.QuitClosure());
737 WaitableEvent event;
738 auto watcher = std::make_unique<WaitableEventWatcher>();
739
740 pump->WatchFileDescriptor(pipefds_[0], false, MessagePumpGlib::WATCH_READ,
741 &controller, &delegate);
742
743 // Make the IO thread wait for |event| before writing to pipefds[1].
744 const char buf = 0;
745 WaitableEventWatcher::EventCallback write_fd_task =
746 BindOnce(&WriteFDWrapper, pipefds_[1], &buf, 1);
747 io_runner()->PostTask(
748 FROM_HERE, BindOnce(IgnoreResult(&WaitableEventWatcher::StartWatching),
749 Unretained(watcher.get()), &event,
750 std::move(write_fd_task), io_runner()));
751
Carlos Caballerob25fe8472020-07-17 10:27:17752 // Queue |event| to signal on |CurrentUIThread::Get()|.
Nick Diego Yamane58914192019-08-08 17:12:48753 ThreadTaskRunnerHandle::Get()->PostTask(
754 FROM_HERE, BindOnce(&WaitableEvent::Signal, Unretained(&event)));
755
756 // Now run the MessageLoop.
757 run_loop.Run();
758
759 // StartWatching can move |watcher| to IO thread. Release on IO thread.
760 io_runner()->PostTask(FROM_HERE, BindOnce(&WaitableEventWatcher::StopWatching,
761 Owned(std::move(watcher))));
762}
763
[email protected]7ff48ca2013-02-06 16:56:19764} // namespace base