blob: 5ed41aef8fa4e42212b3228746bce8ffe21de5ad [file] [log] [blame]
// 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);
FML_DCHECK(result);
}
void MessageLoopLinux::OnEventFired() {
if (TimerDrain(timer_fd_.get())) {
RunExpiredTasksNow();
}
}
} // namespace fml