blob: b41443c1edcf0a4a7eb2ff3da079e4dd9144f4fb [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2012 The Chromium Authors
[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"
Carlos Caballerof5b6f91c2019-12-20 14:07:3818#include "base/memory/ptr_util.h"
Keishi Hattorif28f4f82022-06-21 11:32:1519#include "base/memory/raw_ptr.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"
Nick Diego Yamane58914192019-08-08 17:12:4824#include "base/synchronization/waitable_event.h"
25#include "base/synchronization/waitable_event_watcher.h"
Carlos Caballerob25fe8472020-07-17 10:27:1726#include "base/task/current_thread.h"
Carlos Caballerof5b6f91c2019-12-20 14:07:3827#include "base/task/single_thread_task_executor.h"
Patrick Monette643cdf62021-10-15 19:13:4228#include "base/task/single_thread_task_runner.h"
Carlos Caballerof5b6f91c2019-12-20 14:07:3829#include "base/test/task_environment.h"
[email protected]34b99632011-01-01 01:01:0630#include "base/threading/thread.h"
[email protected]b44d5cc2009-06-15 10:30:4431#include "testing/gtest/include/gtest/gtest.h"
32
[email protected]7ff48ca2013-02-06 16:56:1933namespace base {
[email protected]b44d5cc2009-06-15 10:30:4434namespace {
35
36// This class injects dummy "events" into the GLib loop. When "handled" these
37// events can run tasks. This is intended to mock gtk events (the corresponding
38// GLib source runs at the same priority).
39class EventInjector {
40 public:
41 EventInjector() : processed_events_(0) {
42 source_ = static_cast<Source*>(g_source_new(&SourceFuncs, sizeof(Source)));
43 source_->injector = this;
Ivan Kotenkova16212a52017-11-08 12:37:3344 g_source_attach(source_, nullptr);
[email protected]b44d5cc2009-06-15 10:30:4445 g_source_set_can_recurse(source_, TRUE);
46 }
47
Peter Boström7319bbd2021-09-15 22:59:3848 EventInjector(const EventInjector&) = delete;
49 EventInjector& operator=(const EventInjector&) = delete;
50
[email protected]b44d5cc2009-06-15 10:30:4451 ~EventInjector() {
52 g_source_destroy(source_);
Tom Sepez2a262d0c2022-12-22 02:01:1953 g_source_unref(source_.ExtractAsDangling());
[email protected]b44d5cc2009-06-15 10:30:4454 }
55
56 int HandlePrepare() {
57 // If the queue is empty, block.
58 if (events_.empty())
59 return -1;
[email protected]59e69e742013-06-18 20:27:5260 TimeDelta delta = events_[0].time - Time::NowFromSystemTime();
[email protected]b44d5cc2009-06-15 10:30:4461 return std::max(0, static_cast<int>(ceil(delta.InMillisecondsF())));
62 }
63
64 bool HandleCheck() {
65 if (events_.empty())
66 return false;
[email protected]59e69e742013-06-18 20:27:5267 return events_[0].time <= Time::NowFromSystemTime();
[email protected]b44d5cc2009-06-15 10:30:4468 }
69
70 void HandleDispatch() {
71 if (events_.empty())
72 return;
tzika8cc2202017-04-18 07:01:1573 Event event = std::move(events_[0]);
[email protected]b44d5cc2009-06-15 10:30:4474 events_.erase(events_.begin());
75 ++processed_events_;
[email protected]8f5a7e492012-01-01 02:14:4776 if (!event.callback.is_null())
tzika8cc2202017-04-18 07:01:1577 std::move(event.callback).Run();
[email protected]8f5a7e492012-01-01 02:14:4778 else if (!event.task.is_null())
tzika8cc2202017-04-18 07:01:1579 std::move(event.task).Run();
[email protected]b44d5cc2009-06-15 10:30:4480 }
81
[email protected]763839e2011-12-09 23:06:0282 // Adds an event to the queue. When "handled", executes |callback|.
[email protected]b44d5cc2009-06-15 10:30:4483 // delay_ms is relative to the last event if any, or to Now() otherwise.
tzika8cc2202017-04-18 07:01:1584 void AddEvent(int delay_ms, OnceClosure callback) {
85 AddEventHelper(delay_ms, std::move(callback), OnceClosure());
[email protected]763839e2011-12-09 23:06:0286 }
87
88 void AddDummyEvent(int delay_ms) {
tzika8cc2202017-04-18 07:01:1589 AddEventHelper(delay_ms, OnceClosure(), OnceClosure());
[email protected]763839e2011-12-09 23:06:0290 }
91
tzika8cc2202017-04-18 07:01:1592 void AddEventAsTask(int delay_ms, OnceClosure task) {
93 AddEventHelper(delay_ms, OnceClosure(), std::move(task));
[email protected]b44d5cc2009-06-15 10:30:4494 }
95
96 void Reset() {
97 processed_events_ = 0;
98 events_.clear();
99 }
100
101 int processed_events() const { return processed_events_; }
102
103 private:
104 struct Event {
[email protected]59e69e742013-06-18 20:27:52105 Time time;
tzika8cc2202017-04-18 07:01:15106 OnceClosure callback;
107 OnceClosure task;
[email protected]b44d5cc2009-06-15 10:30:44108 };
109
110 struct Source : public GSource {
Keishi Hattorif28f4f82022-06-21 11:32:15111 raw_ptr<EventInjector> injector;
[email protected]b44d5cc2009-06-15 10:30:44112 };
113
tzika8cc2202017-04-18 07:01:15114 void AddEventHelper(int delay_ms, OnceClosure callback, OnceClosure task) {
[email protected]59e69e742013-06-18 20:27:52115 Time last_time;
[email protected]8f5a7e492012-01-01 02:14:47116 if (!events_.empty())
[email protected]763839e2011-12-09 23:06:02117 last_time = (events_.end()-1)->time;
[email protected]8f5a7e492012-01-01 02:14:47118 else
[email protected]59e69e742013-06-18 20:27:52119 last_time = Time::NowFromSystemTime();
[email protected]8f5a7e492012-01-01 02:14:47120
Peter Kasting53fd6ee2021-10-05 20:40:48121 Time future = last_time + Milliseconds(delay_ms);
tzika8cc2202017-04-18 07:01:15122 EventInjector::Event event = {future, std::move(callback), std::move(task)};
123 events_.push_back(std::move(event));
[email protected]763839e2011-12-09 23:06:02124 }
125
[email protected]b44d5cc2009-06-15 10:30:44126 static gboolean Prepare(GSource* source, gint* timeout_ms) {
127 *timeout_ms = static_cast<Source*>(source)->injector->HandlePrepare();
128 return FALSE;
129 }
130
131 static gboolean Check(GSource* source) {
132 return static_cast<Source*>(source)->injector->HandleCheck();
133 }
134
135 static gboolean Dispatch(GSource* source,
136 GSourceFunc unused_func,
137 gpointer unused_data) {
138 static_cast<Source*>(source)->injector->HandleDispatch();
139 return TRUE;
140 }
141
Keishi Hattorif28f4f82022-06-21 11:32:15142 raw_ptr<Source> source_;
[email protected]b44d5cc2009-06-15 10:30:44143 std::vector<Event> events_;
144 int processed_events_;
145 static GSourceFuncs SourceFuncs;
[email protected]b44d5cc2009-06-15 10:30:44146};
147
Ivan Kotenkova16212a52017-11-08 12:37:33148GSourceFuncs EventInjector::SourceFuncs = {EventInjector::Prepare,
149 EventInjector::Check,
150 EventInjector::Dispatch, nullptr};
[email protected]b44d5cc2009-06-15 10:30:44151
[email protected]b44d5cc2009-06-15 10:30:44152void IncrementInt(int *value) {
153 ++*value;
154}
155
156// Checks how many events have been processed by the injector.
157void ExpectProcessedEvents(EventInjector* injector, int count) {
158 EXPECT_EQ(injector->processed_events(), count);
159}
160
[email protected]b44d5cc2009-06-15 10:30:44161// Posts a task on the current message loop.
Brett Wilson8e88b312017-09-12 05:22:16162void PostMessageLoopTask(const Location& from_here, OnceClosure task) {
Sean Maher7d0e8052022-12-09 01:46:32163 SingleThreadTaskRunner::GetCurrentDefault()->PostTask(from_here,
164 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) {
Sean Maher7d0e8052022-12-09 01:46:32242 SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
Carlos Caballerof5b6f91c2019-12-20 14:07:38243 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;
Sean Maher7d0e8052022-12-09 01:46:32249 SingleThreadTaskRunner::GetCurrentDefault()->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) {
Sean Maher7d0e8052022-12-09 01:46:32261 SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
Peter Kasting53fd6ee2021-10-05 20:40:48262 FROM_HERE, BindOnce(&IncrementInt, &task_count), Milliseconds(10 * i));
[email protected]b44d5cc2009-06-15 10:30:44263 }
264 // After all the previous tasks have executed, enqueue an event that will
265 // quit.
266 // This relies on the fact that delayed tasks are executed in delay order.
267 // That is verified in message_loop_unittest.cc.
Wezfda077b2018-06-07 21:18:11268 {
269 RunLoop run_loop;
Sean Maher7d0e8052022-12-09 01:46:32270 SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
Wezfda077b2018-06-07 21:18:11271 FROM_HERE,
272 BindOnce(&EventInjector::AddEvent, Unretained(injector()), 0,
273 run_loop.QuitClosure()),
Peter Kasting53fd6ee2021-10-05 20:40:48274 Milliseconds(150));
Wezfda077b2018-06-07 21:18:11275 run_loop.Run();
276 }
[email protected]b44d5cc2009-06-15 10:30:44277 ASSERT_EQ(10, task_count);
278 EXPECT_EQ(1, injector()->processed_events());
279}
280
281TEST_F(MessagePumpGLibTest, TestEventsWhileWaitingForWork) {
282 // Tests that we process events while waiting for work.
283 // The event queue is empty at first.
284 for (int i = 0; i < 10; ++i) {
[email protected]763839e2011-12-09 23:06:02285 injector()->AddDummyEvent(0);
[email protected]b44d5cc2009-06-15 10:30:44286 }
287 // After all the events have been processed, post a task that will check that
288 // the events have been processed (note: the task executes after the event
289 // that posted it has been handled, so we expect 11 at that point).
tzika8cc2202017-04-18 07:01:15290 OnceClosure check_task =
291 BindOnce(&ExpectProcessedEvents, Unretained(injector()), 11);
292 OnceClosure posted_task =
293 BindOnce(&PostMessageLoopTask, FROM_HERE, std::move(check_task));
294 injector()->AddEventAsTask(10, std::move(posted_task));
[email protected]b44d5cc2009-06-15 10:30:44295
296 // And then quit (relies on the condition tested by TestEventTaskInterleave).
Wezfda077b2018-06-07 21:18:11297 RunLoop run_loop;
298 injector()->AddEvent(10, run_loop.QuitClosure());
299 run_loop.Run();
[email protected]b44d5cc2009-06-15 10:30:44300
301 EXPECT_EQ(12, injector()->processed_events());
302}
303
304namespace {
305
306// This class is a helper for the concurrent events / posted tasks test below.
307// It will quit the main loop once enough tasks and events have been processed,
308// while making sure there is always work to do and events in the queue.
[email protected]59e69e742013-06-18 20:27:52309class ConcurrentHelper : public RefCounted<ConcurrentHelper> {
[email protected]b44d5cc2009-06-15 10:30:44310 public:
Wezfda077b2018-06-07 21:18:11311 ConcurrentHelper(EventInjector* injector, OnceClosure done_closure)
[email protected]b44d5cc2009-06-15 10:30:44312 : injector_(injector),
Wezfda077b2018-06-07 21:18:11313 done_closure_(std::move(done_closure)),
[email protected]b44d5cc2009-06-15 10:30:44314 event_count_(kStartingEventCount),
Wezfda077b2018-06-07 21:18:11315 task_count_(kStartingTaskCount) {}
[email protected]b44d5cc2009-06-15 10:30:44316
317 void FromTask() {
318 if (task_count_ > 0) {
319 --task_count_;
320 }
321 if (task_count_ == 0 && event_count_ == 0) {
Wezfda077b2018-06-07 21:18:11322 std::move(done_closure_).Run();
[email protected]b44d5cc2009-06-15 10:30:44323 } else {
Sean Maher7d0e8052022-12-09 01:46:32324 SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
tzik92b7a422017-04-11 15:00:44325 FROM_HERE, BindOnce(&ConcurrentHelper::FromTask, this));
[email protected]b44d5cc2009-06-15 10:30:44326 }
327 }
328
329 void FromEvent() {
330 if (event_count_ > 0) {
331 --event_count_;
332 }
333 if (task_count_ == 0 && event_count_ == 0) {
Wezfda077b2018-06-07 21:18:11334 std::move(done_closure_).Run();
[email protected]b44d5cc2009-06-15 10:30:44335 } else {
tzika8cc2202017-04-18 07:01:15336 injector_->AddEventAsTask(0,
337 BindOnce(&ConcurrentHelper::FromEvent, this));
[email protected]b44d5cc2009-06-15 10:30:44338 }
339 }
340
341 int event_count() const { return event_count_; }
342 int task_count() const { return task_count_; }
343
344 private:
[email protected]59e69e742013-06-18 20:27:52345 friend class RefCounted<ConcurrentHelper>;
[email protected]877d55d2009-11-05 21:53:08346
347 ~ConcurrentHelper() {}
348
[email protected]b44d5cc2009-06-15 10:30:44349 static const int kStartingEventCount = 20;
350 static const int kStartingTaskCount = 20;
351
Keishi Hattorif28f4f82022-06-21 11:32:15352 raw_ptr<EventInjector> injector_;
Wezfda077b2018-06-07 21:18:11353 OnceClosure done_closure_;
[email protected]b44d5cc2009-06-15 10:30:44354 int event_count_;
355 int task_count_;
356};
357
358} // namespace
359
360TEST_F(MessagePumpGLibTest, TestConcurrentEventPostedTask) {
361 // Tests that posted tasks don't starve events, nor the opposite.
362 // We use the helper class above. We keep both event and posted task queues
363 // full, the helper verifies that both tasks and events get processed.
364 // If that is not the case, either event_count_ or task_count_ will not get
[email protected]91cae2592013-01-10 14:56:17365 // to 0, and MessageLoop::QuitWhenIdle() will never be called.
Wezfda077b2018-06-07 21:18:11366 RunLoop run_loop;
367 scoped_refptr<ConcurrentHelper> helper =
368 new ConcurrentHelper(injector(), run_loop.QuitClosure());
[email protected]b44d5cc2009-06-15 10:30:44369
370 // Add 2 events to the queue to make sure it is always full (when we remove
371 // the event before processing it).
tzika8cc2202017-04-18 07:01:15372 injector()->AddEventAsTask(0, BindOnce(&ConcurrentHelper::FromEvent, helper));
373 injector()->AddEventAsTask(0, BindOnce(&ConcurrentHelper::FromEvent, helper));
[email protected]b44d5cc2009-06-15 10:30:44374
375 // Similarly post 2 tasks.
Sean Maher7d0e8052022-12-09 01:46:32376 SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
tzik92b7a422017-04-11 15:00:44377 FROM_HERE, BindOnce(&ConcurrentHelper::FromTask, helper));
Sean Maher7d0e8052022-12-09 01:46:32378 SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
tzik92b7a422017-04-11 15:00:44379 FROM_HERE, BindOnce(&ConcurrentHelper::FromTask, helper));
[email protected]b44d5cc2009-06-15 10:30:44380
Wezfda077b2018-06-07 21:18:11381 run_loop.Run();
[email protected]b44d5cc2009-06-15 10:30:44382 EXPECT_EQ(0, helper->event_count());
383 EXPECT_EQ(0, helper->task_count());
384}
385
386namespace {
387
Wezfda077b2018-06-07 21:18:11388void AddEventsAndDrainGLib(EventInjector* injector, OnceClosure on_drained) {
[email protected]b44d5cc2009-06-15 10:30:44389 // Add a couple of dummy events
[email protected]763839e2011-12-09 23:06:02390 injector->AddDummyEvent(0);
391 injector->AddDummyEvent(0);
[email protected]b44d5cc2009-06-15 10:30:44392 // Then add an event that will quit the main loop.
Wezfda077b2018-06-07 21:18:11393 injector->AddEvent(0, std::move(on_drained));
[email protected]b44d5cc2009-06-15 10:30:44394
395 // Post a couple of dummy tasks
Sean Maher7d0e8052022-12-09 01:46:32396 SingleThreadTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE, DoNothing());
397 SingleThreadTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE, DoNothing());
[email protected]b44d5cc2009-06-15 10:30:44398
399 // Drain the events
Ivan Kotenkova16212a52017-11-08 12:37:33400 while (g_main_context_pending(nullptr)) {
401 g_main_context_iteration(nullptr, FALSE);
[email protected]b44d5cc2009-06-15 10:30:44402 }
403}
404
405} // namespace
406
407TEST_F(MessagePumpGLibTest, TestDrainingGLib) {
408 // Tests that draining events using GLib works.
Wezfda077b2018-06-07 21:18:11409 RunLoop run_loop;
Sean Maher7d0e8052022-12-09 01:46:32410 SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
Wezfda077b2018-06-07 21:18:11411 FROM_HERE, BindOnce(&AddEventsAndDrainGLib, Unretained(injector()),
412 run_loop.QuitClosure()));
413 run_loop.Run();
[email protected]b44d5cc2009-06-15 10:30:44414
415 EXPECT_EQ(3, injector()->processed_events());
416}
417
[email protected]b44d5cc2009-06-15 10:30:44418namespace {
419
420// Helper class that lets us run the GLib message loop.
[email protected]59e69e742013-06-18 20:27:52421class GLibLoopRunner : public RefCounted<GLibLoopRunner> {
[email protected]b44d5cc2009-06-15 10:30:44422 public:
423 GLibLoopRunner() : quit_(false) { }
424
425 void RunGLib() {
426 while (!quit_) {
Ivan Kotenkova16212a52017-11-08 12:37:33427 g_main_context_iteration(nullptr, TRUE);
[email protected]b44d5cc2009-06-15 10:30:44428 }
429 }
430
[email protected]258dca42011-09-21 00:17:19431 void RunLoop() {
[email protected]258dca42011-09-21 00:17:19432 while (!quit_) {
Ivan Kotenkova16212a52017-11-08 12:37:33433 g_main_context_iteration(nullptr, TRUE);
[email protected]258dca42011-09-21 00:17:19434 }
[email protected]b44d5cc2009-06-15 10:30:44435 }
436
437 void Quit() {
438 quit_ = true;
439 }
440
441 void Reset() {
442 quit_ = false;
443 }
444
445 private:
[email protected]59e69e742013-06-18 20:27:52446 friend class RefCounted<GLibLoopRunner>;
[email protected]877d55d2009-11-05 21:53:08447
448 ~GLibLoopRunner() {}
449
[email protected]b44d5cc2009-06-15 10:30:44450 bool quit_;
451};
452
Wezfda077b2018-06-07 21:18:11453void TestGLibLoopInternal(EventInjector* injector, OnceClosure done) {
[email protected]b44d5cc2009-06-15 10:30:44454 scoped_refptr<GLibLoopRunner> runner = new GLibLoopRunner();
455
456 int task_count = 0;
457 // Add a couple of dummy events
[email protected]763839e2011-12-09 23:06:02458 injector->AddDummyEvent(0);
459 injector->AddDummyEvent(0);
[email protected]b44d5cc2009-06-15 10:30:44460 // Post a couple of dummy tasks
Sean Maher7d0e8052022-12-09 01:46:32461 SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
462 FROM_HERE, BindOnce(&IncrementInt, &task_count));
463 SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
464 FROM_HERE, BindOnce(&IncrementInt, &task_count));
[email protected]b44d5cc2009-06-15 10:30:44465 // Delayed events
[email protected]763839e2011-12-09 23:06:02466 injector->AddDummyEvent(10);
467 injector->AddDummyEvent(10);
[email protected]b44d5cc2009-06-15 10:30:44468 // Delayed work
Sean Maher7d0e8052022-12-09 01:46:32469 SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
Peter Kasting53fd6ee2021-10-05 20:40:48470 FROM_HERE, BindOnce(&IncrementInt, &task_count), Milliseconds(30));
Sean Maher7d0e8052022-12-09 01:46:32471 SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
Peter Kasting53fd6ee2021-10-05 20:40:48472 FROM_HERE, BindOnce(&GLibLoopRunner::Quit, runner), Milliseconds(40));
[email protected]b44d5cc2009-06-15 10:30:44473
474 // Run a nested, straight GLib message loop.
Gabriel Charettebdbb58e72020-06-03 18:26:06475 {
Francois Doraya06ee172022-11-24 21:09:18476 CurrentThread::ScopedAllowApplicationTasksInNativeNestedLoop allow;
Gabriel Charettebdbb58e72020-06-03 18:26:06477 runner->RunGLib();
478 }
[email protected]b44d5cc2009-06-15 10:30:44479
480 ASSERT_EQ(3, task_count);
481 EXPECT_EQ(4, injector->processed_events());
Wezfda077b2018-06-07 21:18:11482 std::move(done).Run();
[email protected]b44d5cc2009-06-15 10:30:44483}
484
Wezfda077b2018-06-07 21:18:11485void TestGtkLoopInternal(EventInjector* injector, OnceClosure done) {
[email protected]b44d5cc2009-06-15 10:30:44486 scoped_refptr<GLibLoopRunner> runner = new GLibLoopRunner();
487
488 int task_count = 0;
489 // Add a couple of dummy events
[email protected]763839e2011-12-09 23:06:02490 injector->AddDummyEvent(0);
491 injector->AddDummyEvent(0);
[email protected]b44d5cc2009-06-15 10:30:44492 // Post a couple of dummy tasks
Sean Maher7d0e8052022-12-09 01:46:32493 SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
494 FROM_HERE, BindOnce(&IncrementInt, &task_count));
495 SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
496 FROM_HERE, BindOnce(&IncrementInt, &task_count));
[email protected]b44d5cc2009-06-15 10:30:44497 // Delayed events
[email protected]763839e2011-12-09 23:06:02498 injector->AddDummyEvent(10);
499 injector->AddDummyEvent(10);
[email protected]b44d5cc2009-06-15 10:30:44500 // Delayed work
Sean Maher7d0e8052022-12-09 01:46:32501 SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
Peter Kasting53fd6ee2021-10-05 20:40:48502 FROM_HERE, BindOnce(&IncrementInt, &task_count), Milliseconds(30));
Sean Maher7d0e8052022-12-09 01:46:32503 SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
Peter Kasting53fd6ee2021-10-05 20:40:48504 FROM_HERE, BindOnce(&GLibLoopRunner::Quit, runner), Milliseconds(40));
[email protected]b44d5cc2009-06-15 10:30:44505
506 // Run a nested, straight Gtk message loop.
Gabriel Charettebdbb58e72020-06-03 18:26:06507 {
Francois Doraya06ee172022-11-24 21:09:18508 CurrentThread::ScopedAllowApplicationTasksInNativeNestedLoop allow;
Gabriel Charettebdbb58e72020-06-03 18:26:06509 runner->RunLoop();
510 }
[email protected]b44d5cc2009-06-15 10:30:44511
512 ASSERT_EQ(3, task_count);
513 EXPECT_EQ(4, injector->processed_events());
Wezfda077b2018-06-07 21:18:11514 std::move(done).Run();
[email protected]b44d5cc2009-06-15 10:30:44515}
516
517} // namespace
518
519TEST_F(MessagePumpGLibTest, TestGLibLoop) {
[email protected]8f5a7e492012-01-01 02:14:47520 // Tests that events and posted tasks are correctly executed if the message
[email protected]b44d5cc2009-06-15 10:30:44521 // loop is not run by MessageLoop::Run() but by a straight GLib loop.
522 // Note that in this case we don't make strong guarantees about niceness
523 // between events and posted tasks.
Wezfda077b2018-06-07 21:18:11524 RunLoop run_loop;
Sean Maher7d0e8052022-12-09 01:46:32525 SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
Wezfda077b2018-06-07 21:18:11526 FROM_HERE, BindOnce(&TestGLibLoopInternal, Unretained(injector()),
527 run_loop.QuitClosure()));
528 run_loop.Run();
[email protected]b44d5cc2009-06-15 10:30:44529}
530
531TEST_F(MessagePumpGLibTest, TestGtkLoop) {
[email protected]8f5a7e492012-01-01 02:14:47532 // Tests that events and posted tasks are correctly executed if the message
[email protected]b44d5cc2009-06-15 10:30:44533 // loop is not run by MessageLoop::Run() but by a straight Gtk loop.
534 // Note that in this case we don't make strong guarantees about niceness
535 // between events and posted tasks.
Wezfda077b2018-06-07 21:18:11536 RunLoop run_loop;
Sean Maher7d0e8052022-12-09 01:46:32537 SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
Wezfda077b2018-06-07 21:18:11538 FROM_HERE, BindOnce(&TestGtkLoopInternal, Unretained(injector()),
539 run_loop.QuitClosure()));
540 run_loop.Run();
[email protected]b44d5cc2009-06-15 10:30:44541}
[email protected]7ff48ca2013-02-06 16:56:19542
Nick Diego Yamane58914192019-08-08 17:12:48543// Tests for WatchFileDescriptor API
544class MessagePumpGLibFdWatchTest : public testing::Test {
545 protected:
546 MessagePumpGLibFdWatchTest()
547 : io_thread_("MessagePumpGLibFdWatchTestIOThread") {}
548 ~MessagePumpGLibFdWatchTest() override = default;
549
550 void SetUp() override {
551 Thread::Options options(MessagePumpType::IO, 0);
Olivier Lid68d0792021-05-17 20:51:41552 ASSERT_TRUE(io_thread_.StartWithOptions(std::move(options)));
Nick Diego Yamane58914192019-08-08 17:12:48553 int ret = pipe(pipefds_);
554 ASSERT_EQ(0, ret);
555 }
556
557 void TearDown() override {
Reilly Grant6e56f8c2022-01-12 02:45:55558 // Wait for the IO thread to exit before closing FDs which may have been
559 // passed to it.
560 io_thread_.Stop();
Nick Diego Yamane58914192019-08-08 17:12:48561 if (IGNORE_EINTR(close(pipefds_[0])) < 0)
562 PLOG(ERROR) << "close";
563 if (IGNORE_EINTR(close(pipefds_[1])) < 0)
564 PLOG(ERROR) << "close";
565 }
566
567 void WaitUntilIoThreadStarted() {
568 ASSERT_TRUE(io_thread_.WaitUntilThreadStarted());
569 }
570
571 scoped_refptr<SingleThreadTaskRunner> io_runner() const {
572 return io_thread_.task_runner();
573 }
574
575 void SimulateEvent(MessagePumpGlib* pump,
576 MessagePumpGlib::FdWatchController* controller) {
577 controller->poll_fd_->revents = G_IO_IN | G_IO_OUT;
578 pump->HandleFdWatchDispatch(controller);
579 }
580
581 int pipefds_[2];
582
583 private:
584 Thread io_thread_;
585};
586
587namespace {
588
589class BaseWatcher : public MessagePumpGlib::FdWatcher {
590 public:
591 explicit BaseWatcher(MessagePumpGlib::FdWatchController* controller)
592 : controller_(controller) {
593 DCHECK(controller_);
594 }
595 ~BaseWatcher() override = default;
596
597 // base:MessagePumpGlib::FdWatcher interface
598 void OnFileCanReadWithoutBlocking(int /* fd */) override { NOTREACHED(); }
599 void OnFileCanWriteWithoutBlocking(int /* fd */) override { NOTREACHED(); }
600
601 protected:
Keishi Hattorif28f4f82022-06-21 11:32:15602 raw_ptr<MessagePumpGlib::FdWatchController> controller_;
Nick Diego Yamane58914192019-08-08 17:12:48603};
604
605class DeleteWatcher : public BaseWatcher {
606 public:
607 explicit DeleteWatcher(
608 std::unique_ptr<MessagePumpGlib::FdWatchController> controller)
609 : BaseWatcher(controller.get()),
610 owned_controller_(std::move(controller)) {}
611
612 ~DeleteWatcher() override { DCHECK(!controller_); }
613
Tom Sepez2ba31a02023-01-05 18:39:43614 bool HasController() const { return !!controller_; }
615
Nick Diego Yamane58914192019-08-08 17:12:48616 void OnFileCanWriteWithoutBlocking(int /* fd */) override {
Tom Sepez2ba31a02023-01-05 18:39:43617 ClearController();
618 }
619
620 protected:
621 void ClearController() {
Nick Diego Yamane58914192019-08-08 17:12:48622 DCHECK(owned_controller_);
Nick Diego Yamane58914192019-08-08 17:12:48623 controller_ = nullptr;
Tom Sepez2a262d0c2022-12-22 02:01:19624 owned_controller_.reset();
Nick Diego Yamane58914192019-08-08 17:12:48625 }
626
627 private:
628 std::unique_ptr<MessagePumpGlib::FdWatchController> owned_controller_;
629};
630
631class StopWatcher : public BaseWatcher {
632 public:
633 explicit StopWatcher(MessagePumpGlib::FdWatchController* controller)
634 : BaseWatcher(controller) {}
635
636 ~StopWatcher() override = default;
637
638 void OnFileCanWriteWithoutBlocking(int /* fd */) override {
639 controller_->StopWatchingFileDescriptor();
640 }
641};
642
643void QuitMessageLoopAndStart(OnceClosure quit_closure) {
644 std::move(quit_closure).Run();
645
646 RunLoop runloop(RunLoop::Type::kNestableTasksAllowed);
Sean Maher7d0e8052022-12-09 01:46:32647 SingleThreadTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE,
648 runloop.QuitClosure());
Nick Diego Yamane58914192019-08-08 17:12:48649 runloop.Run();
650}
651
652class NestedPumpWatcher : public MessagePumpGlib::FdWatcher {
653 public:
654 NestedPumpWatcher() = default;
655 ~NestedPumpWatcher() override = default;
656
657 void OnFileCanReadWithoutBlocking(int /* fd */) override {
658 RunLoop runloop;
Sean Maher7d0e8052022-12-09 01:46:32659 SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
Nick Diego Yamane58914192019-08-08 17:12:48660 FROM_HERE, BindOnce(&QuitMessageLoopAndStart, runloop.QuitClosure()));
661 runloop.Run();
662 }
663
664 void OnFileCanWriteWithoutBlocking(int /* fd */) override {}
665};
666
Tom Sepez2ba31a02023-01-05 18:39:43667class QuitWatcher : public DeleteWatcher {
Nick Diego Yamane58914192019-08-08 17:12:48668 public:
Tom Sepez2ba31a02023-01-05 18:39:43669 QuitWatcher(std::unique_ptr<MessagePumpGlib::FdWatchController> controller,
Nick Diego Yamane58914192019-08-08 17:12:48670 base::OnceClosure quit_closure)
Tom Sepez2ba31a02023-01-05 18:39:43671 : DeleteWatcher(std::move(controller)),
672 quit_closure_(std::move(quit_closure)) {}
Nick Diego Yamane58914192019-08-08 17:12:48673
Tom Sepez2ba31a02023-01-05 18:39:43674 void OnFileCanReadWithoutBlocking(int fd) override {
675 ClearController();
Nick Diego Yamane58914192019-08-08 17:12:48676 if (quit_closure_)
677 std::move(quit_closure_).Run();
678 }
679
680 private:
681 base::OnceClosure quit_closure_;
682};
683
684void WriteFDWrapper(const int fd,
685 const char* buf,
686 int size,
687 WaitableEvent* event) {
Lei Zhangc9e8a162021-05-14 09:33:52688 ASSERT_TRUE(WriteFileDescriptor(fd, StringPiece(buf, size)));
Nick Diego Yamane58914192019-08-08 17:12:48689}
690
691} // namespace
692
693// Tests that MessagePumpGlib::FdWatcher::OnFileCanReadWithoutBlocking is not
Tom Sepez2ba31a02023-01-05 18:39:43694// called for a READ_WRITE event, and that the controller is destroyed in
Nick Diego Yamane58914192019-08-08 17:12:48695// OnFileCanWriteWithoutBlocking callback.
696TEST_F(MessagePumpGLibFdWatchTest, DeleteWatcher) {
697 auto pump = std::make_unique<MessagePumpGlib>();
698 auto controller_ptr =
699 std::make_unique<MessagePumpGlib::FdWatchController>(FROM_HERE);
700 auto* controller = controller_ptr.get();
701
702 DeleteWatcher watcher(std::move(controller_ptr));
703 pump->WatchFileDescriptor(pipefds_[1], false,
704 MessagePumpGlib::WATCH_READ_WRITE, controller,
705 &watcher);
706
707 SimulateEvent(pump.get(), controller);
Tom Sepez2ba31a02023-01-05 18:39:43708 EXPECT_FALSE(watcher.HasController());
Nick Diego Yamane58914192019-08-08 17:12:48709}
710
711// Tests that MessagePumpGlib::FdWatcher::OnFileCanReadWithoutBlocking is not
712// called for a READ_WRITE event, when the watcher calls
713// StopWatchingFileDescriptor in OnFileCanWriteWithoutBlocking callback.
714TEST_F(MessagePumpGLibFdWatchTest, StopWatcher) {
715 std::unique_ptr<MessagePumpGlib> pump(new MessagePumpGlib);
716 MessagePumpGlib::FdWatchController controller(FROM_HERE);
717 StopWatcher watcher(&controller);
718 pump->WatchFileDescriptor(pipefds_[1], false,
719 MessagePumpGlib::WATCH_READ_WRITE, &controller,
720 &watcher);
721
722 SimulateEvent(pump.get(), &controller);
723}
724
725// Tests that FdWatcher works properly with nested loops.
726TEST_F(MessagePumpGLibFdWatchTest, NestedPumpWatcher) {
Carlos Caballerof5b6f91c2019-12-20 14:07:38727 test::SingleThreadTaskEnvironment task_environment(
728 test::SingleThreadTaskEnvironment::MainThreadType::UI);
Nick Diego Yamane58914192019-08-08 17:12:48729 std::unique_ptr<MessagePumpGlib> pump(new MessagePumpGlib);
Nick Diego Yamane58914192019-08-08 17:12:48730 NestedPumpWatcher watcher;
Tom Sepez2ba31a02023-01-05 18:39:43731 MessagePumpGlib::FdWatchController controller(FROM_HERE);
Nick Diego Yamane58914192019-08-08 17:12:48732 pump->WatchFileDescriptor(pipefds_[1], false, MessagePumpGlib::WATCH_READ,
733 &controller, &watcher);
734
735 SimulateEvent(pump.get(), &controller);
736}
737
738// Tests that MessagePumpGlib quits immediately when it is quit from
739// libevent's event_base_loop().
740TEST_F(MessagePumpGLibFdWatchTest, QuitWatcher) {
Carlos Caballerof5b6f91c2019-12-20 14:07:38741 MessagePumpGlib* pump = new MessagePumpGlib();
742 SingleThreadTaskExecutor executor(WrapUnique(pump));
Nick Diego Yamane58914192019-08-08 17:12:48743 RunLoop run_loop;
Tom Sepez2ba31a02023-01-05 18:39:43744
745 auto owned_controller =
746 std::make_unique<MessagePumpGlib::FdWatchController>(FROM_HERE);
747 MessagePumpGlib::FdWatchController* controller = owned_controller.get();
748 QuitWatcher delegate(std::move(owned_controller), run_loop.QuitClosure());
Nick Diego Yamane58914192019-08-08 17:12:48749
750 pump->WatchFileDescriptor(pipefds_[0], false, MessagePumpGlib::WATCH_READ,
Tom Sepez2ba31a02023-01-05 18:39:43751 controller, &delegate);
Nick Diego Yamane58914192019-08-08 17:12:48752
753 // Make the IO thread wait for |event| before writing to pipefds[1].
754 const char buf = 0;
Tom Sepez2ba31a02023-01-05 18:39:43755 WaitableEvent event;
756 auto watcher = std::make_unique<WaitableEventWatcher>();
Nick Diego Yamane58914192019-08-08 17:12:48757 WaitableEventWatcher::EventCallback write_fd_task =
758 BindOnce(&WriteFDWrapper, pipefds_[1], &buf, 1);
759 io_runner()->PostTask(
760 FROM_HERE, BindOnce(IgnoreResult(&WaitableEventWatcher::StartWatching),
761 Unretained(watcher.get()), &event,
762 std::move(write_fd_task), io_runner()));
763
Carlos Caballerob25fe8472020-07-17 10:27:17764 // Queue |event| to signal on |CurrentUIThread::Get()|.
Sean Maher7d0e8052022-12-09 01:46:32765 SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
Nick Diego Yamane58914192019-08-08 17:12:48766 FROM_HERE, BindOnce(&WaitableEvent::Signal, Unretained(&event)));
767
768 // Now run the MessageLoop.
769 run_loop.Run();
770
771 // StartWatching can move |watcher| to IO thread. Release on IO thread.
772 io_runner()->PostTask(FROM_HERE, BindOnce(&WaitableEventWatcher::StopWatching,
773 Owned(std::move(watcher))));
774}
775
[email protected]7ff48ca2013-02-06 16:56:19776} // namespace base