From: Jérémie Galarneau Date: Wed, 29 Mar 2023 21:15:20 +0000 (-0400) Subject: Add the lttng::eventfd class X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=commitdiff_plain;h=20c734f5b9940033f4ab96e47523c7d9e3d299bb Add the lttng::eventfd class 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 Change-Id: Ie177018ce8ffe24e78f1be82bdd2f77ba9b19e32 --- diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 6aad72cf4..5e5f6d5e7 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -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 index 000000000..685852682 --- /dev/null +++ b/src/common/eventfd.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2023 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include "eventfd.hpp" + +#include +#include +#include + +#include + +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 index 000000000..39d398ce0 --- /dev/null +++ b/src/common/eventfd.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2023 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_EVENTFD_HPP +#define LTTNG_EVENTFD_HPP + +#include + +#include + +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 index 000000000..9491a13af --- /dev/null +++ b/src/common/file-descriptor.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2023 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include "file-descriptor.hpp" + +#include +#include +#include +#include + +#include +#include +#include + +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(), std::declval(), std::declval())); + constexpr auto max_supported_write_size = + std::numeric_limits::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(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(), std::declval(), std::declval())); + constexpr auto max_supported_read_size = std::numeric_limits::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(read_ret) != size) { + LTTNG_THROW_POSIX(fmt::format("Failed to read from file descriptor: fd={}", _fd()), + errno); + } +} diff --git a/src/common/file-descriptor.hpp b/src/common/file-descriptor.hpp index e8ddfe7a1..164842f86 100644 --- a/src/common/file-descriptor.hpp +++ b/src/common/file-descriptor.hpp @@ -8,75 +8,45 @@ #ifndef LTTNG_FILE_DESCRIPTOR_HPP #define LTTNG_FILE_DESCRIPTOR_HPP -#include -#include - -#include - -#include +#include 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; }; diff --git a/src/common/random.cpp b/src/common/random.cpp index b282d67d0..dfab9538c 100644 --- a/src/common/random.cpp +++ b/src/common/random.cpp @@ -5,6 +5,7 @@ * */ +#include #include #include #include @@ -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;