pw_async2_epoll: Use unordered_map; silence persistent warnings

Rather than adding and removing read and write wakers to a vector, use
an unordered_map that maps a file descriptor to read and write wakers.

Also, silence unnecessary warning logs. Previously, epoll dispatcher
frequently emitted these warnings:

  WRN Received an event for registered file descriptor 4, but there is
      no task to wake

This warning would occur with every read. This is because epoll emits
both EPOLLIN and EPOLLOUT events when reading from a read/write channel,
resulting in unhandled write events, even though no tasks are trying to
write.

Retain the log as a debug-level log, and only trigger it if no tasks
were waiting for either reads or writes.

Change-Id: I241776d8d9f6cce01fbdf95babebd48bd5bd81fe
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/218860
Reviewed-by: Alexei Frolov <frolv@google.com>
Lint: Lint 🤖 <android-build-ayeaye@system.gserviceaccount.com>
Pigweed-Auto-Submit: Wyatt Hepler <hepler@google.com>
Commit-Queue: Wyatt Hepler <hepler@google.com>
This commit is contained in:
Wyatt Hepler 2024-06-28 22:55:12 +00:00 committed by CQ Bot Account
parent 8b8ad3820d
commit 62da50a4f4
3 changed files with 27 additions and 52 deletions

View File

@ -257,7 +257,7 @@ class Waker {
friend class DispatcherImpl;
public:
Waker() = default;
constexpr Waker() = default;
Waker(Waker&& other) noexcept PW_LOCKS_EXCLUDED(dispatcher_lock());
/// Replace this ``Waker`` with another.

View File

@ -124,15 +124,24 @@ Status Dispatcher::NativeWaitForWake() {
PW_CHECK_INT_EQ(
bytes_read, 1, "Dispatcher failed to read wake notification");
PW_DCHECK_INT_EQ(unused, kNotificationSignal);
} else {
if ((event.events & (EPOLLIN | EPOLLRDHUP)) != 0) {
NativeFindAndWakeFileDescriptor(event.data.fd,
FileDescriptorType::kReadable);
}
if ((event.events & EPOLLOUT) != 0) {
NativeFindAndWakeFileDescriptor(event.data.fd,
FileDescriptorType::kWritable);
}
continue;
}
// Debug log for missed events.
if (PW_LOG_LEVEL >= PW_LOG_LEVEL_DEBUG &&
wakers_[event.data.fd].read.IsEmpty() &&
wakers_[event.data.fd].write.IsEmpty()) {
PW_LOG_DEBUG(
"Received an event for registered file descriptor %d, but there is "
"no task to wake",
event.data.fd);
}
if ((event.events & (EPOLLIN | EPOLLRDHUP)) != 0) {
std::move(wakers_[event.data.fd].read).Wake();
}
if ((event.events & EPOLLOUT) != 0) {
std::move(wakers_[event.data.fd].write).Wake();
}
}
@ -167,35 +176,10 @@ Status Dispatcher::NativeUnregisterFileDescriptor(int fd) {
PW_LOG_ERROR("Failed to unregister epoll event: %s", std::strerror(errno));
return Status::Internal();
}
auto fd_waker = std::find_if(fd_wakers_.begin(),
fd_wakers_.end(),
[fd](auto& f) { return f.fd == fd; });
if (fd_waker != fd_wakers_.end()) {
fd_wakers_.erase(fd_waker);
}
wakers_.erase(fd);
return OkStatus();
}
void Dispatcher::NativeFindAndWakeFileDescriptor(int fd,
FileDescriptorType type) {
auto fd_waker =
std::find_if(fd_wakers_.begin(), fd_wakers_.end(), [fd, type](auto& f) {
return f.fd == fd && f.type == type;
});
if (fd_waker == fd_wakers_.end()) {
PW_LOG_WARN(
"Received an event for registered file descriptor %d, but there is no "
"task to wake",
fd);
return;
}
std::move(fd_waker->waker).Wake();
fd_wakers_.erase(fd_waker);
}
void Dispatcher::DoWake() {
// Perform a write to unblock the waiting dispatcher.
ssize_t bytes_written = write(notify_fd_, &kNotificationSignal, 1);

View File

@ -13,7 +13,7 @@
// the License.
#pragma once
#include <vector>
#include <unordered_map>
#include "pw_assert/assert.h"
#include "pw_async2/dispatcher_base.h"
@ -41,22 +41,19 @@ class Dispatcher final : public DispatcherImpl<Dispatcher> {
Status NativeUnregisterFileDescriptor(int fd);
void NativeAddReadWakerForFileDescriptor(int fd, Waker&& waker) {
NativeAddWakerForFileDescriptor(
fd, FileDescriptorType::kReadable, std::move(waker));
wakers_[fd].read = std::move(waker);
}
void NativeAddWriteWakerForFileDescriptor(int fd, Waker&& waker) {
NativeAddWakerForFileDescriptor(
fd, FileDescriptorType::kWritable, std::move(waker));
wakers_[fd].write = std::move(waker);
}
private:
static constexpr size_t kMaxEventsToProcessAtOnce = 5;
struct FdWaker {
int fd;
FileDescriptorType type;
Waker waker;
struct ReadWriteWaker {
Waker read;
Waker write;
};
void DoWake() final;
@ -67,17 +64,11 @@ class Dispatcher final : public DispatcherImpl<Dispatcher> {
Status NativeWaitForWake();
void NativeFindAndWakeFileDescriptor(int fd, FileDescriptorType type);
void NativeAddWakerForFileDescriptor(int fd,
FileDescriptorType type,
Waker&& waker) {
fd_wakers_.push_back({fd, type, std::move(waker)});
}
int epoll_fd_;
int notify_fd_;
int wait_fd_;
std::vector<FdWaker> fd_wakers_;
std::unordered_map<int, ReadWriteWaker> wakers_;
};
} // namespace pw::async2