179 lines
5.6 KiB
C++
179 lines
5.6 KiB
C++
/*
|
|
* Copyright (C) 2022 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "common/libs/fs/epoll.h"
|
|
|
|
#include <sys/epoll.h>
|
|
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <set>
|
|
#include <shared_mutex>
|
|
|
|
#include "common/libs/fs/shared_fd.h"
|
|
#include "common/libs/utils/result.h"
|
|
|
|
namespace cuttlefish {
|
|
|
|
Result<Epoll> Epoll::Create() {
|
|
int fd = epoll_create1(EPOLL_CLOEXEC);
|
|
if (fd == -1) {
|
|
return CF_ERRNO("Failed to create epoll");
|
|
}
|
|
SharedFD shared{std::shared_ptr<FileInstance>(new FileInstance(fd, 0))};
|
|
return Epoll(shared);
|
|
}
|
|
|
|
Epoll::Epoll() = default;
|
|
|
|
Epoll::Epoll(SharedFD epoll_fd) : epoll_fd_(epoll_fd) {}
|
|
|
|
Epoll::Epoll(Epoll&& other) {
|
|
std::unique_lock own_watched(watched_mutex_, std::defer_lock);
|
|
std::unique_lock own_epoll(epoll_mutex_, std::defer_lock);
|
|
std::unique_lock other_epoll(other.epoll_mutex_, std::defer_lock);
|
|
std::unique_lock other_watched(other.watched_mutex_, std::defer_lock);
|
|
std::lock(own_watched, own_epoll, other_epoll, other_watched);
|
|
|
|
epoll_fd_ = std::move(other.epoll_fd_);
|
|
watched_ = std::move(other.watched_);
|
|
}
|
|
|
|
Epoll& Epoll::operator=(Epoll&& other) {
|
|
std::unique_lock own_watched(watched_mutex_, std::defer_lock);
|
|
std::unique_lock own_epoll(epoll_mutex_, std::defer_lock);
|
|
std::unique_lock other_epoll(other.epoll_mutex_, std::defer_lock);
|
|
std::unique_lock other_watched(other.watched_mutex_, std::defer_lock);
|
|
std::lock(own_watched, own_epoll, other_epoll, other_watched);
|
|
|
|
epoll_fd_ = std::move(other.epoll_fd_);
|
|
watched_ = std::move(other.watched_);
|
|
return *this;
|
|
}
|
|
|
|
Result<void> Epoll::Add(SharedFD fd, uint32_t events) {
|
|
std::unique_lock watched_lock(watched_mutex_, std::defer_lock);
|
|
std::shared_lock epoll_lock(epoll_mutex_, std::defer_lock);
|
|
std::lock(watched_lock, epoll_lock);
|
|
CF_EXPECT(epoll_fd_->IsOpen(), "Empty Epoll instance");
|
|
|
|
if (watched_.count(fd) != 0) {
|
|
return CF_ERRNO("Watched set already contains fd");
|
|
}
|
|
epoll_event event;
|
|
event.events = events;
|
|
event.data.fd = fd->fd_;
|
|
int success = epoll_ctl(epoll_fd_->fd_, EPOLL_CTL_ADD, fd->fd_, &event);
|
|
if (success != 0 && errno == EEXIST) {
|
|
// We're already tracking this fd, don't drop it from the set.
|
|
return CF_ERRNO("epoll_ctl: File descriptor was already present");
|
|
} else if (success != 0) {
|
|
return CF_ERRNO("epoll_ctl: Add failed");
|
|
}
|
|
watched_.insert(fd);
|
|
return {};
|
|
}
|
|
|
|
Result<void> Epoll::AddOrModify(SharedFD fd, uint32_t events) {
|
|
std::unique_lock watched_lock(watched_mutex_, std::defer_lock);
|
|
std::shared_lock epoll_lock(epoll_mutex_, std::defer_lock);
|
|
std::lock(watched_lock, epoll_lock);
|
|
CF_EXPECT(epoll_fd_->IsOpen(), "Empty Epoll instance");
|
|
|
|
epoll_event event;
|
|
event.events = events;
|
|
event.data.fd = fd->fd_;
|
|
int operation = watched_.count(fd) == 0 ? EPOLL_CTL_ADD : EPOLL_CTL_MOD;
|
|
int success = epoll_ctl(epoll_fd_->fd_, operation, fd->fd_, &event);
|
|
if (success != 0) {
|
|
std::string operation_str = operation == EPOLL_CTL_ADD ? "add" : "modify";
|
|
return CF_ERRNO("epoll_ctl: Operation " << operation_str << " failed");
|
|
}
|
|
watched_.insert(fd);
|
|
return {};
|
|
}
|
|
|
|
Result<void> Epoll::Modify(SharedFD fd, uint32_t events) {
|
|
std::unique_lock watched_lock(watched_mutex_, std::defer_lock);
|
|
std::shared_lock epoll_lock(epoll_mutex_, std::defer_lock);
|
|
std::lock(watched_lock, epoll_lock);
|
|
CF_EXPECT(epoll_fd_->IsOpen(), "Empty Epoll instance");
|
|
|
|
if (watched_.count(fd) == 0) {
|
|
return CF_ERR("Watched set did not contain fd");
|
|
}
|
|
epoll_event event;
|
|
event.events = events;
|
|
event.data.fd = fd->fd_;
|
|
int success = epoll_ctl(epoll_fd_->fd_, EPOLL_CTL_MOD, fd->fd_, &event);
|
|
if (success != 0) {
|
|
return CF_ERRNO("epoll_ctl: Modify failed");
|
|
}
|
|
return {};
|
|
}
|
|
|
|
Result<void> Epoll::Delete(SharedFD fd) {
|
|
std::unique_lock watched_lock(watched_mutex_, std::defer_lock);
|
|
std::shared_lock epoll_lock(epoll_mutex_, std::defer_lock);
|
|
std::lock(watched_lock, epoll_lock);
|
|
CF_EXPECT(epoll_fd_->IsOpen(), "Empty Epoll instance");
|
|
|
|
if (watched_.count(fd) == 0) {
|
|
return CF_ERR("Watched set did not contain fd");
|
|
}
|
|
int success = epoll_ctl(epoll_fd_->fd_, EPOLL_CTL_DEL, fd->fd_, nullptr);
|
|
if (success != 0) {
|
|
return CF_ERRNO("epoll_ctl: Delete failed");
|
|
}
|
|
watched_.erase(fd);
|
|
return {};
|
|
}
|
|
|
|
Result<std::optional<EpollEvent>> Epoll::Wait() {
|
|
epoll_event event;
|
|
int success;
|
|
{
|
|
std::shared_lock lock(epoll_mutex_);
|
|
CF_EXPECT(epoll_fd_->IsOpen(), "Empty Epoll instance");
|
|
success = epoll_wait(epoll_fd_->fd_, &event, 1, -1);
|
|
}
|
|
if (success == -1) {
|
|
return CF_ERRNO("epoll_wait failed");
|
|
} else if (success == 0) {
|
|
return {};
|
|
} else if (success != 1) {
|
|
return CF_ERR("epoll_wait returned an unexpected value");
|
|
}
|
|
EpollEvent ret;
|
|
ret.events = event.events;
|
|
std::shared_lock lock(watched_mutex_);
|
|
for (const auto& watched : watched_) {
|
|
if (watched->fd_ == event.data.fd) {
|
|
ret.fd = watched;
|
|
break;
|
|
}
|
|
}
|
|
if (!ret.fd->IsOpen()) {
|
|
// Couldn't find the matching SharedFD to the file descriptor. We probably
|
|
// lost the race to lock watched_mutex_ against a delete call. Treat this
|
|
// as a spurious wakeup.
|
|
return {};
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
} // namespace cuttlefish
|