From 117679f92e5c2859361d482ca285594e47ee57c4 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 20 Mar 2017 14:49:10 -0700 Subject: [PATCH] The Linux FML backend. (#3488) --- fml/platform/linux/message_loop_linux.cc | 90 ++++++++++++++++++++++++ fml/platform/linux/message_loop_linux.h | 43 +++++++++++ fml/platform/linux/timerfd.cc | 58 +++++++++++++++ fml/platform/linux/timerfd.h | 53 ++++++++++++++ travis/licenses_golden/licenses_flutter | 4 ++ 5 files changed, 248 insertions(+) create mode 100644 fml/platform/linux/message_loop_linux.cc create mode 100644 fml/platform/linux/message_loop_linux.h create mode 100644 fml/platform/linux/timerfd.cc create mode 100644 fml/platform/linux/timerfd.h diff --git a/fml/platform/linux/message_loop_linux.cc b/fml/platform/linux/message_loop_linux.cc new file mode 100644 index 000000000..70a94dd41 --- /dev/null +++ b/fml/platform/linux/message_loop_linux.cc @@ -0,0 +1,90 @@ +// Copyright 2017 The Chromium 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 +#include + +#include "flutter/fml/platform/linux/timerfd.h" +#include "lib/ftl/files/eintr_wrapper.h" + +namespace fml { + +static constexpr int kClockType = CLOCK_MONOTONIC; + +MessageLoopLinux::MessageLoopLinux() + : epoll_fd_(HANDLE_EINTR(::epoll_create(1 /* unused */))), + timer_fd_(::timerfd_create(kClockType, TFD_NONBLOCK | TFD_CLOEXEC)), + running_(false) { + FTL_CHECK(epoll_fd_.is_valid()); + FTL_CHECK(timer_fd_.is_valid()); + bool added_source = AddOrRemoveTimerSource(true); + FTL_CHECK(added_source); +} + +MessageLoopLinux::~MessageLoopLinux() { + bool removed_source = AddOrRemoveTimerSource(false); + FTL_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; +} + +void MessageLoopLinux::Run() { + running_ = true; + + while (running_) { + struct epoll_event event = {}; + + int epoll_result = 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(); + } + } +} + +void MessageLoopLinux::Terminate() { + running_ = false; + WakeUp(ftl::TimePoint::Now()); +} + +void MessageLoopLinux::WakeUp(ftl::TimePoint time_point) { + bool result = TimerRearm(timer_fd_.get(), time_point); + FTL_DCHECK(result); +} + +void MessageLoopLinux::OnEventFired() { + if (TimerDrain(timer_fd_.get())) { + RunExpiredTasksNow(); + } +} + +} // namespace fml diff --git a/fml/platform/linux/message_loop_linux.h b/fml/platform/linux/message_loop_linux.h new file mode 100644 index 000000000..f47a0615d --- /dev/null +++ b/fml/platform/linux/message_loop_linux.h @@ -0,0 +1,43 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FML_PLATFORM_LINUX_MESSAGE_LOOP_LINUX_H_ +#define FLUTTER_FML_PLATFORM_LINUX_MESSAGE_LOOP_LINUX_H_ + +#include + +#include "flutter/fml/message_loop_impl.h" +#include "lib/ftl/files/unique_fd.h" +#include "lib/ftl/macros.h" + +namespace fml { + +class MessageLoopLinux : public MessageLoopImpl { + private: + ftl::UniqueFD epoll_fd_; + ftl::UniqueFD timer_fd_; + bool running_; + + MessageLoopLinux(); + + ~MessageLoopLinux() override; + + void Run() override; + + void Terminate() override; + + void WakeUp(ftl::TimePoint time_point) override; + + void OnEventFired(); + + bool AddOrRemoveTimerSource(bool add); + + FRIEND_MAKE_REF_COUNTED(MessageLoopLinux); + FRIEND_REF_COUNTED_THREAD_SAFE(MessageLoopLinux); + FTL_DISALLOW_COPY_AND_ASSIGN(MessageLoopLinux); +}; + +} // namespace fml + +#endif // FLUTTER_FML_PLATFORM_LINUX_MESSAGE_LOOP_LINUX_H_ diff --git a/fml/platform/linux/timerfd.cc b/fml/platform/linux/timerfd.cc new file mode 100644 index 000000000..e3137ea60 --- /dev/null +++ b/fml/platform/linux/timerfd.cc @@ -0,0 +1,58 @@ +// Copyright 2017 The Chromium 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/timerfd.h" + +#include +#include + +#include "lib/ftl/files/eintr_wrapper.h" + +#if FML_TIMERFD_AVAILABLE == 0 + +#include +#include + +int timerfd_create(int clockid, int flags) { + return syscall(__NR_timerfd_create, clockid, flags); +} + +int timerfd_settime(int ufc, + int flags, + const struct itimerspec* utmr, + struct itimerspec* otmr) { + return syscall(__NR_timerfd_settime, ufc, flags, utmr, otmr); +} + +#endif // FML_TIMERFD_AVAILABLE == 0 + +namespace fml { + +#ifndef NSEC_PER_SEC +#define NSEC_PER_SEC 1000000000 +#endif + +bool TimerRearm(int fd, ftl::TimePoint time_point) { + const uint64_t nano_secs = time_point.ToEpochDelta().ToNanoseconds(); + + struct itimerspec spec = {}; + spec.it_value.tv_sec = (time_t)(nano_secs / NSEC_PER_SEC); + spec.it_value.tv_nsec = nano_secs % NSEC_PER_SEC; + spec.it_interval = spec.it_value; // single expiry. + + int result = ::timerfd_settime(fd, TFD_TIMER_ABSTIME, &spec, nullptr); + return result == 0; +} + +bool TimerDrain(int fd) { + // 8 bytes must be read from a signalled timer file descriptor when signalled. + uint64_t fire_count = 0; + ssize_t size = HANDLE_EINTR(::read(fd, &fire_count, sizeof(uint64_t))); + if (size != sizeof(uint64_t)) { + return false; + } + return fire_count > 0; +} + +} // namespace fml diff --git a/fml/platform/linux/timerfd.h b/fml/platform/linux/timerfd.h new file mode 100644 index 000000000..c8c6b7aa0 --- /dev/null +++ b/fml/platform/linux/timerfd.h @@ -0,0 +1,53 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FML_PLATFORM_LINUX_TIMER_FD_H_ +#define FLUTTER_FML_PLATFORM_LINUX_TIMER_FD_H_ + +#include "lib/ftl/time/time_point.h" + +// clang-format off +#if __has_include() +// clang-format on + +#include + +#define FML_TIMERFD_AVAILABLE 1 + +#else // __has_include() + +#define FML_TIMERFD_AVAILABLE 0 + +#include +// Must come after sys/types +#include + +#define TFD_TIMER_ABSTIME (1 << 0) +#define TFD_TIMER_CANCEL_ON_SET (1 << 1) + +#define TFD_CLOEXEC O_CLOEXEC +#define TFD_NONBLOCK O_NONBLOCK + +int timerfd_create(int clockid, int flags); + +int timerfd_settime(int ufc, + int flags, + const struct itimerspec* utmr, + struct itimerspec* otmr); + +#endif // __has_include() + +namespace fml { + +/// Rearms the timer to expire at the given time point. +bool TimerRearm(int fd, ftl::TimePoint time_point); + +/// Drains the timer FD and retuns true if it has expired. This may be false in +/// case the timer read is non-blocking and this routine was called before the +/// timer expiry. +bool TimerDrain(int fd); + +} // namespace fml + +#endif // FLUTTER_FML_PLATFORM_LINUX_TIMER_FD_H_ diff --git a/travis/licenses_golden/licenses_flutter b/travis/licenses_golden/licenses_flutter index a03484eba..5621737c2 100644 --- a/travis/licenses_golden/licenses_flutter +++ b/travis/licenses_golden/licenses_flutter @@ -1917,6 +1917,10 @@ FILE: ../../../flutter/fml/message_loop_impl.h FILE: ../../../flutter/fml/message_loop_unittests.cc FILE: ../../../flutter/fml/platform/darwin/cf_utils.cc FILE: ../../../flutter/fml/platform/darwin/cf_utils.h +FILE: ../../../flutter/fml/platform/linux/message_loop_linux.cc +FILE: ../../../flutter/fml/platform/linux/message_loop_linux.h +FILE: ../../../flutter/fml/platform/linux/timerfd.cc +FILE: ../../../flutter/fml/platform/linux/timerfd.h FILE: ../../../flutter/fml/task_runner.cc FILE: ../../../flutter/fml/task_runner.h FILE: ../../../flutter/fml/thread.cc -- GitLab