Add the lttng::eventfd class
authorJérémie Galarneau <jeremie.galarneau@efficios.com>
Wed, 29 Mar 2023 21:15:20 +0000 (17:15 -0400)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Tue, 11 Apr 2023 20:26:58 +0000 (16:26 -0400)
Add an RAII wrapper around the use of eventfd(), extending the
file_descriptor class. The implementation of file_descriptor is moved to
its own TU to make clean-up the interface.

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
Change-Id: Ie177018ce8ffe24e78f1be82bdd2f77ba9b19e32

src/common/Makefile.am
src/common/eventfd.cpp [new file with mode: 0644]
src/common/eventfd.hpp [new file with mode: 0644]
src/common/file-descriptor.cpp [new file with mode: 0644]
src/common/file-descriptor.hpp
src/common/random.cpp

index 6aad72cf4dd65291398ea22932973c46e968eece..5e5f6d5e79985d8da98a98432016118ae7beebac 100644 (file)
@@ -73,6 +73,7 @@ libcommon_lgpl_la_SOURCES = \
        error-query.cpp \
        evaluation.cpp \
        event.cpp \
+       eventfd.hpp eventfd.cpp \
        event-expr/event-expr.cpp \
        event-field-value.cpp \
        event-rule/event-rule.cpp \
@@ -85,7 +86,7 @@ libcommon_lgpl_la_SOURCES = \
        event-rule/jul-logging.cpp \
        event-rule/python-logging.cpp \
        exception.cpp exception.hpp \
-       file-descriptor.hpp \
+       file-descriptor.hpp file-descriptor.cpp \
        fd-handle.cpp fd-handle.hpp\
        format.hpp \
        kernel-probe.cpp \
diff --git a/src/common/eventfd.cpp b/src/common/eventfd.cpp
new file mode 100644 (file)
index 0000000..6858526
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2023 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include "eventfd.hpp"
+
+#include <common/exception.hpp>
+#include <common/format.hpp>
+#include <common/readwrite.hpp>
+
+#include <sys/eventfd.h>
+
+lttng::eventfd::eventfd(bool use_semaphore_semantics, std::uint64_t initial_value) :
+       file_descriptor([use_semaphore_semantics, initial_value]() {
+               int flags = ::EFD_CLOEXEC;
+
+               if (use_semaphore_semantics) {
+                       flags |= ::EFD_SEMAPHORE;
+               }
+
+               const auto raw_fd = ::eventfd(initial_value, flags);
+               if (raw_fd < 0) {
+                       LTTNG_THROW_POSIX("Failed to create eventfd", errno);
+               }
+
+               return raw_fd;
+       }())
+{
+}
+
+void lttng::eventfd::increment(std::uint64_t value)
+{
+       try {
+               write(&value, sizeof(value));
+       } catch (const std::exception& e) {
+               LTTNG_THROW_ERROR(fmt::format("Failed to increment eventfd: {}", e.what()));
+       }
+}
+
+std::uint64_t lttng::eventfd::decrement()
+{
+       std::uint64_t value;
+
+       try {
+               read(&value, sizeof(value));
+       } catch (const std::exception& e) {
+               LTTNG_THROW_ERROR(fmt::format("Failed to decrement eventfd: {}", e.what()));
+       }
+
+       return value;
+}
diff --git a/src/common/eventfd.hpp b/src/common/eventfd.hpp
new file mode 100644 (file)
index 0000000..39d398c
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2023 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_EVENTFD_HPP
+#define LTTNG_EVENTFD_HPP
+
+#include <common/file-descriptor.hpp>
+
+#include <cstdint>
+
+namespace lttng {
+
+class eventfd : public file_descriptor {
+public:
+       /* Throws a posix_error exception on failure to create the underlying resource. */
+       eventfd(bool use_semaphore_semantics = true, std::uint64_t initial_value = 0);
+       eventfd(const eventfd&) = delete;
+       eventfd& operator=(const eventfd&) = delete;
+       eventfd(eventfd&&) = delete;
+       void operator=(eventfd&&) = delete;
+
+       /* Throws on error. */
+       void increment(std::uint64_t value = 1);
+       /*
+        * Note that decrement() will block if the underlying value of the eventfd is 0 when
+        * semaphore semantics are used, see EVENTFD(2).
+        *
+        * decrement() returns the new value of the underlying counter of the eventfd.
+        *
+        * Throws on error.
+        */
+       std::uint64_t decrement();
+};
+
+} /* namespace lttng */
+
+#endif /* LTTNG_EVENTFD_HPP */
diff --git a/src/common/file-descriptor.cpp b/src/common/file-descriptor.cpp
new file mode 100644 (file)
index 0000000..9491a13
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2023 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include "file-descriptor.hpp"
+
+#include <common/error.hpp>
+#include <common/exception.hpp>
+#include <common/format.hpp>
+#include <common/readwrite.hpp>
+
+#include <algorithm>
+#include <limits>
+#include <unistd.h>
+
+namespace {
+bool is_valid_fd(int fd)
+{
+       return fd >= 0;
+}
+} // anonymous namespace
+
+lttng::file_descriptor::file_descriptor() noexcept
+{
+}
+
+lttng::file_descriptor::file_descriptor(int raw_fd) noexcept : _raw_fd{ raw_fd }
+{
+       LTTNG_ASSERT(is_valid_fd(_raw_fd));
+}
+
+lttng::file_descriptor::file_descriptor(lttng::file_descriptor&& other) noexcept
+{
+       std::swap(_raw_fd, other._raw_fd);
+}
+
+lttng::file_descriptor& lttng::file_descriptor::operator=(lttng::file_descriptor&& other) noexcept
+{
+       _cleanup();
+       std::swap(_raw_fd, other._raw_fd);
+       return *this;
+}
+
+lttng::file_descriptor::~file_descriptor() noexcept
+{
+       _cleanup();
+}
+
+int lttng::file_descriptor::_fd() const noexcept
+{
+       LTTNG_ASSERT(is_valid_fd(_raw_fd));
+       return _raw_fd;
+}
+
+void lttng::file_descriptor::_cleanup() noexcept
+{
+       if (!is_valid_fd(_raw_fd)) {
+               return;
+       }
+
+       const auto ret = ::close(_raw_fd);
+
+       _raw_fd = -1;
+       if (ret) {
+               PERROR("Failed to close file descriptor: fd=%i", _raw_fd);
+       }
+}
+
+void lttng::file_descriptor::write(const void *buffer, std::size_t size)
+{
+       /*
+        * This is a limitation of the internal helper that is not a problem in practice for the
+        * moment.
+        */
+       using lttng_write_return_type = decltype(lttng_write(
+               std::declval<int>(), std::declval<const void *>(), std::declval<size_t>()));
+       constexpr auto max_supported_write_size =
+               std::numeric_limits<lttng_write_return_type>::max();
+
+       if (size > max_supported_write_size) {
+               LTTNG_THROW_UNSUPPORTED_ERROR(fmt::format(
+                       "Write size exceeds the maximal supported value of lttng_write: write_size={}, maximal_write_size={}",
+                       size,
+                       max_supported_write_size));
+       }
+
+       const auto write_ret = lttng_write(_fd(), buffer, size);
+       if (write_ret < 0 || static_cast<std::size_t>(write_ret) != size) {
+               LTTNG_THROW_POSIX(fmt::format("Failed to write to file descriptor: fd={}", _fd()),
+                                 errno);
+       }
+}
+
+void lttng::file_descriptor::read(void *buffer, std::size_t size)
+{
+       /*
+        * This is a limitation of the internal helper that is not a problem in practice for the
+        * moment.
+        */
+       using lttng_read_return_type = decltype(lttng_read(
+               std::declval<int>(), std::declval<void *>(), std::declval<size_t>()));
+       constexpr auto max_supported_read_size = std::numeric_limits<lttng_read_return_type>::max();
+
+       if (size > max_supported_read_size) {
+               LTTNG_THROW_UNSUPPORTED_ERROR(fmt::format(
+                       "Read size exceeds the maximal supported value of lttng_read: read_size={}, maximal_read_size={}",
+                       size,
+                       max_supported_read_size));
+       }
+
+       const auto read_ret = lttng_read(_fd(), buffer, size);
+       if (read_ret < 0 || static_cast<std::size_t>(read_ret) != size) {
+               LTTNG_THROW_POSIX(fmt::format("Failed to read from file descriptor: fd={}", _fd()),
+                                 errno);
+       }
+}
index e8ddfe7a144815c15c8b729fd53027e0e923bd9d..164842f86ba8184d2cec46715a43be031ba8e606 100644 (file)
@@ -8,75 +8,45 @@
 #ifndef LTTNG_FILE_DESCRIPTOR_HPP
 #define LTTNG_FILE_DESCRIPTOR_HPP
 
-#include <common/error.hpp>
-#include <common/format.hpp>
-
-#include <algorithm>
-
-#include <unistd.h>
+#include <cstddef>
 
 namespace lttng {
 
-/*
- * RAII wrapper around a UNIX file descriptor. A file_descriptor's underlying
- * file descriptor.
- */
+/* RAII wrapper around a UNIX file descriptor. */
 class file_descriptor {
 public:
-       file_descriptor()
-       {
-       }
-
-       explicit file_descriptor(int raw_fd) noexcept : _raw_fd{ raw_fd }
-       {
-               LTTNG_ASSERT(_is_valid_fd(_raw_fd));
-       }
+       file_descriptor() noexcept;
 
+       explicit file_descriptor(int raw_fd) noexcept;
        file_descriptor(const file_descriptor&) = delete;
        file_descriptor& operator=(const file_descriptor&) = delete;
-       file_descriptor& operator=(file_descriptor&& other)
-       {
-               _cleanup();
-               std::swap(_raw_fd, other._raw_fd);
-               return *this;
-       }
 
-       file_descriptor(file_descriptor&& other) noexcept
-       {
-               std::swap(_raw_fd, other._raw_fd);
-       }
+       file_descriptor(file_descriptor&& other) noexcept;
 
-       ~file_descriptor()
-       {
-               _cleanup();
-       }
+       file_descriptor& operator=(file_descriptor&& other) noexcept;
 
-       int fd() const noexcept
-       {
-               LTTNG_ASSERT(_is_valid_fd(_raw_fd));
-               return _raw_fd;
-       }
+       ~file_descriptor() noexcept;
 
-private:
-       static bool _is_valid_fd(int fd)
-       {
-               return fd >= 0;
-       }
-
-       void _cleanup()
-       {
-               if (!_is_valid_fd(_raw_fd)) {
-                       return;
-               }
+       /*
+        * Read `size` bytes from the underlying file descriptor, assuming
+        * raw_fd behaves as a blocking device.
+        *
+        * Throws an exception if the requested amount of bytes couldn't be read.
+        */
+       void read(void *buffer, std::size_t size);
+       /*
+        * Write `size` bytes to the underlying file descriptor, assuming
+        * raw_fd behaves as a blocking device.
+        *
+        * Throws an exception if the requested amount of bytes couldn't be written.
+        */
+       void write(const void *buffer, std::size_t size);
 
-               const auto ret = ::close(_raw_fd);
-
-               _raw_fd = -1;
-               if (ret) {
-                       PERROR("Failed to close file descriptor: fd=%i", _raw_fd);
-               }
-       }
+protected:
+       int _fd() const noexcept;
+       void _cleanup() noexcept;
 
+private:
        int _raw_fd = -1;
 };
 
index b282d67d0157386c26e8343709865bf66145d817..dfab9538c0cab31cd4f556547a9f4e7b261d7fe3 100644 (file)
@@ -5,6 +5,7 @@
  *
  */
 
+#include <common/error.hpp>
 #include <common/file-descriptor.hpp>
 #include <common/format.hpp>
 #include <common/hashtable/utils.hpp>
@@ -138,11 +139,11 @@ lttng::random::seed_t produce_random_seed_from_urandom()
        }() };
 
        lttng::random::seed_t seed;
-       const auto read_ret = lttng_read(urandom.fd(), &seed, sizeof(seed));
-       if (read_ret != sizeof(seed)) {
-               LTTNG_THROW_POSIX(fmt::format("Failed to read from `/dev/urandom`: size={}",
-                                             sizeof(seed)),
-                                 errno);
+       try {
+               urandom.read(&seed, sizeof(seed));
+       } catch (const std::exception& e) {
+               LTTNG_THROW_RANDOM_PRODUCTION_ERROR(fmt::format(
+                       "Failed to read from `/dev/urandom`: size={}: {}", sizeof(seed), e.what()));
        }
 
        return seed;
This page took 0.041144 seconds and 4 git commands to generate.