Skip to content

Commit

Permalink
filedescriptor: implent a new FileDescriptor class
Browse files Browse the repository at this point in the history
makes you able to RAII the filedescriptor making it somewhat easier to
not leak.
  • Loading branch information
gulafaran committed Nov 21, 2024
1 parent e911361 commit d4b96a0
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 0 deletions.
8 changes: 8 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ add_test(
COMMAND hyprutils_os "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
install(TARGETS hyprutils)
install(DIRECTORY "include/hyprutils" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
Expand Down
39 changes: 39 additions & 0 deletions include/hyprutils/utils/FileDescriptor.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#pragma once

#include <fcntl.h>
namespace Hyprutils {
namespace Utils {
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 changes: 89 additions & 0 deletions src/utils/FileDescriptor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#include <cstdlib>
#include <hyprutils/utils/FileDescriptor.hpp>
#include <fcntl.h>
#include <sys/poll.h>
#include <unistd.h>
#include <utility>

using namespace Hyprutils::Utils;

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 changes: 49 additions & 0 deletions tests/filedescriptor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#include <hyprutils/utils/FileDescriptor.hpp>
#include "shared.hpp"
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>

using namespace Hyprutils::Utils;

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;
}

0 comments on commit d4b96a0

Please sign in to comment.