// // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #include "td/utils/port/detail/EventFdLinux.h" char disable_linker_warning_about_empty_file_event_fd_linux_cpp TD_UNUSED; #ifdef TD_EVENTFD_LINUX #include "td/utils/logging.h" #include "td/utils/misc.h" #include "td/utils/port/detail/NativeFd.h" #include "td/utils/port/detail/skip_eintr.h" #include "td/utils/port/PollFlags.h" #include "td/utils/ScopeGuard.h" #include "td/utils/Slice.h" #include "td/utils/SliceBuilder.h" #include #include #include #include namespace td { namespace detail { class EventFdLinuxImpl { public: PollableFdInfo info_; }; EventFdLinux::EventFdLinux() = default; EventFdLinux::EventFdLinux(EventFdLinux &&) noexcept = default; EventFdLinux &EventFdLinux::operator=(EventFdLinux &&) noexcept = default; EventFdLinux::~EventFdLinux() = default; void EventFdLinux::init() { auto fd = NativeFd(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC)); auto eventfd_errno = errno; LOG_IF(FATAL, !fd) << Status::PosixError(eventfd_errno, "eventfd call failed"); impl_ = make_unique(); impl_->info_.set_native_fd(std::move(fd)); } bool EventFdLinux::empty() { return !impl_; } void EventFdLinux::close() { impl_.reset(); } Status EventFdLinux::get_pending_error() { return Status::OK(); } PollableFdInfo &EventFdLinux::get_poll_info() { return impl_->info_; } // NB: will be called from multiple threads void EventFdLinux::release() { const uint64 value = 1; auto slice = Slice(reinterpret_cast(&value), sizeof(value)); auto native_fd = impl_->info_.native_fd().fd(); auto result = [&]() -> Result { auto write_res = detail::skip_eintr([&] { return write(native_fd, slice.begin(), slice.size()); }); auto write_errno = errno; if (write_res >= 0) { return narrow_cast(write_res); } return Status::PosixError(write_errno, PSLICE() << "Write to fd " << native_fd << " has failed"); }(); if (result.is_error()) { LOG(FATAL) << "EventFdLinux write failed: " << result.error(); } size_t size = result.ok(); if (size != sizeof(value)) { LOG(FATAL) << "EventFdLinux write returned " << value << " instead of " << sizeof(value); } } void EventFdLinux::acquire() { impl_->info_.sync_with_poll(); SCOPE_EXIT { // Clear flags without EAGAIN and EWOULDBLOCK // Looks like it is safe thing to do with eventfd get_poll_info().clear_flags(PollFlags::Read()); }; uint64 res; auto slice = MutableSlice(reinterpret_cast(&res), sizeof(res)); auto native_fd = impl_->info_.native_fd().fd(); auto result = [&]() -> Result { CHECK(!slice.empty()); auto read_res = detail::skip_eintr([&] { return ::read(native_fd, slice.begin(), slice.size()); }); auto read_errno = errno; if (read_res >= 0) { CHECK(read_res != 0); return narrow_cast(read_res); } if (read_errno == EAGAIN #if EAGAIN != EWOULDBLOCK || read_errno == EWOULDBLOCK #endif ) { return 0; } return Status::PosixError(read_errno, PSLICE() << "Read from fd " << native_fd << " has failed"); }(); if (result.is_error()) { LOG(FATAL) << "EventFdLinux read failed: " << result.error(); } } void EventFdLinux::wait(int timeout_ms) { detail::skip_eintr_timeout( [this](int timeout_ms) { pollfd fd; fd.fd = get_poll_info().native_fd().fd(); fd.events = POLLIN; return poll(&fd, 1, timeout_ms); }, timeout_ms); } } // namespace detail } // namespace td #endif