134 lines
3.7 KiB
C++
134 lines
3.7 KiB
C++
//
|
|
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
|
|
//
|
|
// 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 <cerrno>
|
|
|
|
#include <poll.h>
|
|
#include <sys/eventfd.h>
|
|
#include <unistd.h>
|
|
|
|
namespace td {
|
|
namespace detail {
|
|
class EventFdLinuxImpl {
|
|
public:
|
|
PollableFdInfo info;
|
|
};
|
|
|
|
EventFdLinux::EventFdLinux() = default;
|
|
EventFdLinux::EventFdLinux(EventFdLinux &&) = default;
|
|
EventFdLinux &EventFdLinux::operator=(EventFdLinux &&) = 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<EventFdLinuxImpl>();
|
|
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<const char *>(&value), sizeof(value));
|
|
auto native_fd = impl_->info.native_fd().fd();
|
|
|
|
auto result = [&]() -> Result<size_t> {
|
|
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<size_t>(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<char *>(&res), sizeof(res));
|
|
auto native_fd = impl_->info.native_fd().fd();
|
|
auto result = [&]() -> Result<size_t> {
|
|
CHECK(slice.size() > 0);
|
|
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<size_t>(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
|