| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "flutter/fml/platform/linux/message_loop_linux.h" |
| |
| #include <sys/epoll.h> |
| #include <unistd.h> |
| |
| #include "flutter/fml/eintr_wrapper.h" |
| #include "flutter/fml/platform/linux/timerfd.h" |
| |
| namespace fml { |
| |
| static constexpr int kClockType = CLOCK_MONOTONIC; |
| |
| MessageLoopLinux::MessageLoopLinux() |
| : epoll_fd_(FML_HANDLE_EINTR(::epoll_create(1 /* unused */))), |
| timer_fd_(::timerfd_create(kClockType, TFD_NONBLOCK | TFD_CLOEXEC)), |
| running_(false) { |
| FML_CHECK(epoll_fd_.is_valid()); |
| FML_CHECK(timer_fd_.is_valid()); |
| bool added_source = AddOrRemoveTimerSource(true); |
| FML_CHECK(added_source); |
| } |
| |
| MessageLoopLinux::~MessageLoopLinux() { |
| bool removed_source = AddOrRemoveTimerSource(false); |
| FML_CHECK(removed_source); |
| } |
| |
| bool MessageLoopLinux::AddOrRemoveTimerSource(bool add) { |
| struct epoll_event event = {}; |
| |
| event.events = EPOLLIN; |
| // The data is just for informational purposes so we know when we were worken |
| // by the FD. |
| event.data.fd = timer_fd_.get(); |
| |
| int ctl_result = |
| ::epoll_ctl(epoll_fd_.get(), add ? EPOLL_CTL_ADD : EPOLL_CTL_DEL, |
| timer_fd_.get(), &event); |
| return ctl_result == 0; |
| } |
| |
| // |fml::MessageLoopImpl| |
| void MessageLoopLinux::Run() { |
| running_ = true; |
| |
| while (running_) { |
| struct epoll_event event = {}; |
| |
| int epoll_result = FML_HANDLE_EINTR( |
| ::epoll_wait(epoll_fd_.get(), &event, 1, -1 /* timeout */)); |
| |
| // Errors are fatal. |
| if (event.events & (EPOLLERR | EPOLLHUP)) { |
| running_ = false; |
| continue; |
| } |
| |
| // Timeouts are fatal since we specified an infinite timeout already. |
| // Likewise, > 1 is not possible since we waited for one result. |
| if (epoll_result != 1) { |
| running_ = false; |
| continue; |
| } |
| |
| if (event.data.fd == timer_fd_.get()) { |
| OnEventFired(); |
| } |
| } |
| } |
| |
| // |fml::MessageLoopImpl| |
| void MessageLoopLinux::Terminate() { |
| running_ = false; |
| WakeUp(fml::TimePoint::Now()); |
| } |
| |
| // |fml::MessageLoopImpl| |
| void MessageLoopLinux::WakeUp(fml::TimePoint time_point) { |
| bool result = TimerRearm(timer_fd_.get(), time_point); |
| (void)result; |
| FML_DCHECK(result); |
| } |
| |
| void MessageLoopLinux::OnEventFired() { |
| if (TimerDrain(timer_fd_.get())) { |
| RunExpiredTasksNow(); |
| } |
| } |
| |
| } // namespace fml |