os: implent a new FileDescriptor class (#21)

makes you able to RAII the filedescriptor making it somewhat easier to
not leak.
This commit is contained in:
Tom Englund 2024-11-21 16:26:34 +01:00 committed by GitHub
parent e911361a68
commit 2e21319c8e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 185 additions and 0 deletions

View file

@ -86,6 +86,14 @@ add_test(
COMMAND hyprutils_os "os") COMMAND hyprutils_os "os")
add_dependencies(tests hyprutils_os) add_dependencies(tests hyprutils_os)
add_executable(hyprutils_filedescriptor "tests/filedescriptor.cpp")
target_link_libraries(hyprutils_filedescriptor PRIVATE hyprutils PkgConfig::deps)
add_test(
NAME "Filedescriptor"
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
COMMAND hyprutils_filedescriptor "filedescriptor")
add_dependencies(tests hyprutils_filedescriptor)
# Installation # Installation
install(TARGETS hyprutils) install(TARGETS hyprutils)
install(DIRECTORY "include/hyprutils" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install(DIRECTORY "include/hyprutils" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})

View file

@ -0,0 +1,39 @@
#pragma once
#include <fcntl.h>
namespace Hyprutils {
namespace OS {
class CFileDescriptor {
public:
CFileDescriptor() = default;
explicit CFileDescriptor(int const fd);
CFileDescriptor(CFileDescriptor&&);
CFileDescriptor& operator=(CFileDescriptor&&);
~CFileDescriptor();
CFileDescriptor(const CFileDescriptor&) = delete;
CFileDescriptor& operator=(const CFileDescriptor&) = delete;
bool operator==(const CFileDescriptor& rhs) const {
return m_fd == rhs.m_fd;
}
bool isValid() const;
int get() const;
int getFlags() const;
bool setFlags(int flags);
int take();
void reset();
CFileDescriptor duplicate(int flags = F_DUPFD_CLOEXEC) const;
bool isReadable() const;
bool isClosed() const;
static bool isReadable(int fd);
static bool isClosed(int fd);
private:
int m_fd = -1;
};
};
};

89
src/os/FileDescriptor.cpp Normal file
View file

@ -0,0 +1,89 @@
#include <cstdlib>
#include <hyprutils/os/FileDescriptor.hpp>
#include <fcntl.h>
#include <sys/poll.h>
#include <unistd.h>
#include <utility>
using namespace Hyprutils::OS;
CFileDescriptor::CFileDescriptor(int const fd) : m_fd(fd) {}
CFileDescriptor::CFileDescriptor(CFileDescriptor&& other) : m_fd(std::exchange(other.m_fd, -1)) {}
CFileDescriptor& CFileDescriptor::operator=(CFileDescriptor&& other) {
if (this == &other) // Shit will go haywire if there is duplicate ownership
abort();
reset();
m_fd = std::exchange(other.m_fd, -1);
return *this;
}
CFileDescriptor::~CFileDescriptor() {
reset();
}
bool CFileDescriptor::isValid() const {
return m_fd != -1;
}
int CFileDescriptor::get() const {
return m_fd;
}
int CFileDescriptor::getFlags() const {
return fcntl(m_fd, F_GETFD);
}
bool CFileDescriptor::setFlags(int flags) {
if (fcntl(m_fd, F_SETFD, flags) == -1)
return false;
return true;
}
int CFileDescriptor::take() {
return std::exchange(m_fd, -1);
}
void CFileDescriptor::reset() {
if (m_fd != -1) {
close(m_fd);
m_fd = -1;
}
}
CFileDescriptor CFileDescriptor::duplicate(int flags) const {
if (m_fd == -1)
return {};
return CFileDescriptor{fcntl(m_fd, flags, 0)};
}
bool CFileDescriptor::isClosed() const {
return isClosed(m_fd);
}
bool CFileDescriptor::isReadable() const {
return isReadable(m_fd);
}
bool CFileDescriptor::isClosed(int fd) {
pollfd pfd = {
.fd = fd,
.events = POLLIN,
.revents = 0,
};
if (poll(&pfd, 1, 0) < 0)
return true;
return pfd.revents & (POLLHUP | POLLERR);
}
bool CFileDescriptor::isReadable(int fd) {
pollfd pfd = {.fd = fd, .events = POLLIN, .revents = 0};
return poll(&pfd, 1, 0) > 0 && (pfd.revents & POLLIN);
}

49
tests/filedescriptor.cpp Normal file
View file

@ -0,0 +1,49 @@
#include <hyprutils/os/FileDescriptor.hpp>
#include "shared.hpp"
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
using namespace Hyprutils::OS;
int main(int argc, char** argv, char** envp) {
std::string name = "/test_filedescriptors";
CFileDescriptor fd(shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600));
int ret = 0;
EXPECT(fd.isValid(), true);
EXPECT(fd.isReadable(), true);
int flags = fd.getFlags();
EXPECT(fd.getFlags(), FD_CLOEXEC);
flags &= ~FD_CLOEXEC;
fd.setFlags(flags);
EXPECT(fd.getFlags(), !FD_CLOEXEC);
CFileDescriptor fd2 = fd.duplicate();
EXPECT(fd.isValid(), true);
EXPECT(fd.isReadable(), true);
EXPECT(fd2.isValid(), true);
EXPECT(fd2.isReadable(), true);
CFileDescriptor fd3(fd2.take());
EXPECT(fd.isValid(), true);
EXPECT(fd.isReadable(), true);
EXPECT(fd2.isValid(), false);
EXPECT(fd2.isReadable(), false);
// .duplicate default flags is FD_CLOEXEC
EXPECT(fd3.getFlags(), FD_CLOEXEC);
fd.reset();
fd2.reset();
fd3.reset();
EXPECT(fd.isReadable(), false);
EXPECT(fd2.isReadable(), false);
EXPECT(fd3.isReadable(), false);
shm_unlink(name.c_str());
return ret;
}