1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "flutter/fml/platform/linux/message_loop_linux.h"
6
7 #include <sys/epoll.h>
8 #include <unistd.h>
9
10 #include "flutter/fml/eintr_wrapper.h"
11 #include "flutter/fml/platform/linux/timerfd.h"
12
13 namespace fml {
14
15 static constexpr int kClockType = CLOCK_MONOTONIC;
16
MessageLoopLinux()17 MessageLoopLinux::MessageLoopLinux()
18 : epoll_fd_(FML_HANDLE_EINTR(::epoll_create(1 /* unused */))),
19 timer_fd_(::timerfd_create(kClockType, TFD_NONBLOCK | TFD_CLOEXEC)),
20 running_(false) {
21 FML_CHECK(epoll_fd_.is_valid());
22 FML_CHECK(timer_fd_.is_valid());
23 bool added_source = AddOrRemoveTimerSource(true);
24 FML_CHECK(added_source);
25 }
26
~MessageLoopLinux()27 MessageLoopLinux::~MessageLoopLinux() {
28 bool removed_source = AddOrRemoveTimerSource(false);
29 FML_CHECK(removed_source);
30 }
31
AddOrRemoveTimerSource(bool add)32 bool MessageLoopLinux::AddOrRemoveTimerSource(bool add) {
33 struct epoll_event event = {};
34
35 event.events = EPOLLIN;
36 // The data is just for informational purposes so we know when we were worken
37 // by the FD.
38 event.data.fd = timer_fd_.get();
39
40 int ctl_result =
41 ::epoll_ctl(epoll_fd_.get(), add ? EPOLL_CTL_ADD : EPOLL_CTL_DEL,
42 timer_fd_.get(), &event);
43 return ctl_result == 0;
44 }
45
46 // |fml::MessageLoopImpl|
Run()47 void MessageLoopLinux::Run() {
48 running_ = true;
49
50 while (running_) {
51 struct epoll_event event = {};
52
53 int epoll_result = FML_HANDLE_EINTR(
54 ::epoll_wait(epoll_fd_.get(), &event, 1, -1 /* timeout */));
55
56 // Errors are fatal.
57 if (event.events & (EPOLLERR | EPOLLHUP)) {
58 running_ = false;
59 continue;
60 }
61
62 // Timeouts are fatal since we specified an infinite timeout already.
63 // Likewise, > 1 is not possible since we waited for one result.
64 if (epoll_result != 1) {
65 running_ = false;
66 continue;
67 }
68
69 if (event.data.fd == timer_fd_.get()) {
70 OnEventFired();
71 }
72 }
73 }
74
75 // |fml::MessageLoopImpl|
Terminate()76 void MessageLoopLinux::Terminate() {
77 running_ = false;
78 WakeUp(fml::TimePoint::Now());
79 }
80
81 // |fml::MessageLoopImpl|
WakeUp(fml::TimePoint time_point)82 void MessageLoopLinux::WakeUp(fml::TimePoint time_point) {
83 bool result = TimerRearm(timer_fd_.get(), time_point);
84 FML_DCHECK(result);
85 }
86
OnEventFired()87 void MessageLoopLinux::OnEventFired() {
88 if (TimerDrain(timer_fd_.get())) {
89 RunExpiredTasksNow();
90 }
91 }
92
93 } // namespace fml
94