From: Remi Gacogne Date: Fri, 10 Jun 2022 15:34:56 +0000 (+0200) Subject: Move channel files from pdns/dnsdistdist to pdns X-Git-Tag: rec-5.0.0-alpha1~161^2~16 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7f98f4ba738265a6f51b4d315201abb9b60756e2;p=thirdparty%2Fpdns.git Move channel files from pdns/dnsdistdist to pdns --- diff --git a/pdns/Makefile.am b/pdns/Makefile.am index 88229c32a7..f9e8c632cd 100644 --- a/pdns/Makefile.am +++ b/pdns/Makefile.am @@ -1602,6 +1602,7 @@ fuzz_target_proxyprotocol_LDFLAGS = $(fuzz_targets_ldflags) fuzz_target_proxyprotocol_LDADD = $(fuzz_targets_libs) fuzz_target_dnsdistcache_SOURCES = \ + channel.hh channel.cc \ dns.cc dns.hh \ dnsdist-cache.cc dnsdist-cache.hh \ dnsdist-ecs.cc dnsdist-ecs.hh \ diff --git a/pdns/channel.cc b/pdns/channel.cc new file mode 100644 index 0000000000..dd25c6d34e --- /dev/null +++ b/pdns/channel.cc @@ -0,0 +1,105 @@ +/* + * This file is part of PowerDNS or dnsdist. + * Copyright -- PowerDNS.COM B.V. and its contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * In addition, for the avoidance of any doubt, permission is granted to + * link this program with OpenSSL and to (re)distribute the binaries + * produced as the result of such linking. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "channel.hh" + +namespace pdns +{ +namespace channel +{ + + Notifier::Notifier(FDWrapper&& fd) : + d_fd(std::move(fd)) + { + } + + bool Notifier::notify() const + { + char data = 'a'; + auto sent = write(d_fd.getHandle(), &data, sizeof(data)); + if (sent != sizeof(data)) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return false; + } + else { + throw std::runtime_error("Unable to write to channel notifier pipe: " + stringerror()); + } + } + return true; + } + + Waiter::Waiter(FDWrapper&& fd) : + d_fd(std::move(fd)) + { + } + + void Waiter::clear() const + { + ssize_t got; + do { + char data; + got = read(d_fd.getHandle(), &data, sizeof(data)); + if (got == 0) { + throw std::runtime_error("EOF while clearing channel notifier pipe"); + } + else if (got == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + break; + } + throw std::runtime_error("Error while clearing channel notifier pipe: " + stringerror()); + } + } while (got); + } + + int Waiter::getDescriptor() const + { + return d_fd.getHandle(); + } + + std::pair createNotificationQueue(bool nonBlocking, size_t pipeBufferSize) + { + int fds[2] = {-1, -1}; + if (pipe(fds) < 0) { + throw std::runtime_error("Error creating notification channel pipe: " + stringerror()); + } + + FDWrapper sender(fds[1]); + FDWrapper receiver(fds[0]); + + if (nonBlocking && !setNonBlocking(receiver.getHandle())) { + int err = errno; + throw std::runtime_error("Error making notification channel pipe non-blocking: " + stringerror(err)); + } + + if (nonBlocking && !setNonBlocking(sender.getHandle())) { + int err = errno; + throw std::runtime_error("Error making notification channel pipe non-blocking: " + stringerror(err)); + } + + if (pipeBufferSize > 0 && getPipeBufferSize(receiver.getHandle()) < pipeBufferSize) { + setPipeBufferSize(receiver.getHandle(), pipeBufferSize); + } + + return std::pair(Notifier(std::move(sender)), Waiter(std::move(receiver))); + } +} +} diff --git a/pdns/channel.hh b/pdns/channel.hh new file mode 100644 index 0000000000..5c352870ed --- /dev/null +++ b/pdns/channel.hh @@ -0,0 +1,285 @@ +/* + * This file is part of PowerDNS or dnsdist. + * Copyright -- PowerDNS.COM B.V. and its contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * In addition, for the avoidance of any doubt, permission is granted to + * link this program with OpenSSL and to (re)distribute the binaries + * produced as the result of such linking. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#pragma once +#include +#include + +#include "misc.hh" + +namespace pdns +{ +namespace channel +{ + /** + * The sender's end of a channel used to pass objects between threads. + * + * A sender can be used by several threads in a safe way. + */ + template > + class Sender + { + public: + Sender() + { + } + Sender(FDWrapper&& fd) : + d_fd(std::move(fd)) + { + } + Sender(const Sender&) = delete; + Sender& operator=(const Sender&) = delete; + Sender(Sender&&) = default; + Sender& operator=(Sender&&) = default; + /** + * \brief Try to send the supplied object to the other end of that channel. Might block if the channel was created in blocking mode. + * + * \return True if the object was properly sent, False if the channel is full. + * + * \throw runtime_error if the channel is broken, for example if the other end has been closed. + */ + bool send(std::unique_ptr&&) const; + + private: + FDWrapper d_fd; + }; + + /** + * The receiver's end of a channel used to pass objects between threads. + * + * A receiver can be used by several threads in a safe way, but in that case spurious wake up might happen. + */ + template > + class Receiver + { + public: + Receiver() + { + } + Receiver(FDWrapper&& fd) : + d_fd(std::move(fd)) + { + } + Receiver(const Receiver&) = delete; + Receiver& operator=(const Receiver&) = delete; + Receiver(Receiver&&) = default; + Receiver& operator=(Receiver&&) = default; + /** + * \brief Try to read an object sent by the other end of that channel. Might block if the channel was created in blocking mode. + * + * \return An object if one was available, and std::nullopt otherwise. + * + * \throw runtime_error if the channel is broken, for example if the other end has been closed. + */ + std::optional> receive() const; + std::optional> receive(D deleter) const; + + /** + * \brief Get a descriptor that can be used with an I/O multiplexer to wait for an object to become available. + * + * \return A valid descriptor or -1 if the Receiver was not properly initialized. + */ + int getDescriptor() const + { + return d_fd.getHandle(); + } + + private: + FDWrapper d_fd; + }; + + /** + * \brief Create a channel to pass objects between threads, accepting multiple senders and receivers. + * + * \return A pair of Sender and Receiver objects. + * + * \throw runtime_error if the channel creation failed. + */ + template > + std::pair, Receiver> createObjectQueue(bool nonBlocking = true, size_t pipeBufferSize = 0); + + /** + * The notifier's end of a channel used to communicate between threads. + * + * A notifier can be used by several threads in a safe way. + */ + class Notifier + { + public: + Notifier() + { + } + Notifier(FDWrapper&&); + Notifier(const Notifier&) = delete; + Notifier& operator=(const Notifier&) = delete; + Notifier(Notifier&&) = default; + Notifier& operator=(Notifier&&) = default; + + /** + * \brief Queue a notification to wake up the other end of the channel. + * + * \return True if the notification was properly sent, False if the channel is full. + * + * \throw runtime_error if the channel is broken, for example if the other end has been closed. + */ + bool notify() const; + + private: + FDWrapper d_fd; + }; + + /** + * The waiter's end of a channel used to communicate between threads. + * + * A waiter can be used by several threads in a safe way, but in that case spurious wake up might happen. + */ + class Waiter + { + public: + Waiter(FDWrapper&&); + Waiter(const Waiter&) = delete; + Waiter& operator=(const Waiter&) = delete; + Waiter(Waiter&&) = default; + Waiter& operator=(Waiter&&) = default; + + /** + * \brief Clear all notifications queued on that channel, if any. + */ + void clear() const; + /** + * \brief Get a descriptor that can be used with an I/O multiplexer to wait for a notification to arrive. + * + * \return A valid descriptor or -1 if the Waiter was not properly initialized. + */ + int getDescriptor() const; + + private: + FDWrapper d_fd; + }; + + /** + * \brief Create a channel to notify one thread from another one, accepting multiple senders and receivers. + * + * \return A pair of Notifier and Sender objects. + * + * \throw runtime_error if the channel creation failed. + */ + std::pair createNotificationQueue(bool nonBlocking = true, size_t pipeBufferSize = 0); + + template + bool Sender::send(std::unique_ptr&& object) const + { + // we do not release right away because we might need the custom deleter later + auto ptr = object.get(); + static_assert(sizeof(ptr) <= PIPE_BUF, "Writes up to PIPE_BUF are guaranted not to interleaved and to either fully succeed or fail"); + ssize_t sent = write(d_fd.getHandle(), &ptr, sizeof(ptr)); + + if (sent == sizeof(ptr)) { + // we cannot touch it anymore + object.release(); + } + else { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return false; + } + else { + throw std::runtime_error("Unable to write to channel:" + stringerror()); + } + } + + return true; + } + + template + std::optional> Receiver::receive() const + { + std::optional> result; + T* obj{nullptr}; + ssize_t got = read(d_fd.getHandle(), &obj, sizeof(obj)); + if (got == sizeof(obj)) { + return std::unique_ptr(obj); + } + else if (got == 0) { + throw std::runtime_error("EOF while reading from Channel receiver"); + } + else if (got == -1) { + if (errno == EAGAIN || errno == EINTR) { + return result; + } + throw std::runtime_error("Error while reading from Channel receiver: " + stringerror()); + } + else { + throw std::runtime_error("Partial read from Channel receiver"); + } + } + + template + std::optional> Receiver::receive(D deleter) const + { + std::optional> result; + T* obj{nullptr}; + ssize_t got = read(d_fd.getHandle(), &obj, sizeof(obj)); + if (got == sizeof(obj)) { + return std::unique_ptr(obj, deleter); + } + else if (got == 0) { + throw std::runtime_error("EOF while reading from Channel receiver"); + } + else if (got == -1) { + if (errno == EAGAIN || errno == EINTR) { + return result; + } + throw std::runtime_error("Error while reading from Channel receiver: " + stringerror()); + } + else { + throw std::runtime_error("Partial read from Channel receiver"); + } + } + + template + std::pair, Receiver> createObjectQueue(bool nonBlocking, size_t pipeBufferSize) + { + int fds[2] = {-1, -1}; + if (pipe(fds) < 0) { + throw std::runtime_error("Error creating channel pipe: " + stringerror()); + } + + FDWrapper sender(fds[1]); + FDWrapper receiver(fds[0]); + + if (nonBlocking && !setNonBlocking(receiver.getHandle())) { + int err = errno; + throw std::runtime_error("Error making channel pipe non-blocking: " + stringerror(err)); + } + + if (nonBlocking && !setNonBlocking(sender.getHandle())) { + int err = errno; + throw std::runtime_error("Error making channel pipe non-blocking: " + stringerror(err)); + } + + if (pipeBufferSize > 0 && getPipeBufferSize(receiver.getHandle()) < pipeBufferSize) { + setPipeBufferSize(receiver.getHandle(), pipeBufferSize); + } + + return std::pair(Sender(std::move(sender)), Receiver(std::move(receiver))); + } +} +} diff --git a/pdns/dnsdistdist/channel.cc b/pdns/dnsdistdist/channel.cc deleted file mode 100644 index dd25c6d34e..0000000000 --- a/pdns/dnsdistdist/channel.cc +++ /dev/null @@ -1,105 +0,0 @@ -/* - * This file is part of PowerDNS or dnsdist. - * Copyright -- PowerDNS.COM B.V. and its contributors - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * In addition, for the avoidance of any doubt, permission is granted to - * link this program with OpenSSL and to (re)distribute the binaries - * produced as the result of such linking. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "channel.hh" - -namespace pdns -{ -namespace channel -{ - - Notifier::Notifier(FDWrapper&& fd) : - d_fd(std::move(fd)) - { - } - - bool Notifier::notify() const - { - char data = 'a'; - auto sent = write(d_fd.getHandle(), &data, sizeof(data)); - if (sent != sizeof(data)) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { - return false; - } - else { - throw std::runtime_error("Unable to write to channel notifier pipe: " + stringerror()); - } - } - return true; - } - - Waiter::Waiter(FDWrapper&& fd) : - d_fd(std::move(fd)) - { - } - - void Waiter::clear() const - { - ssize_t got; - do { - char data; - got = read(d_fd.getHandle(), &data, sizeof(data)); - if (got == 0) { - throw std::runtime_error("EOF while clearing channel notifier pipe"); - } - else if (got == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { - break; - } - throw std::runtime_error("Error while clearing channel notifier pipe: " + stringerror()); - } - } while (got); - } - - int Waiter::getDescriptor() const - { - return d_fd.getHandle(); - } - - std::pair createNotificationQueue(bool nonBlocking, size_t pipeBufferSize) - { - int fds[2] = {-1, -1}; - if (pipe(fds) < 0) { - throw std::runtime_error("Error creating notification channel pipe: " + stringerror()); - } - - FDWrapper sender(fds[1]); - FDWrapper receiver(fds[0]); - - if (nonBlocking && !setNonBlocking(receiver.getHandle())) { - int err = errno; - throw std::runtime_error("Error making notification channel pipe non-blocking: " + stringerror(err)); - } - - if (nonBlocking && !setNonBlocking(sender.getHandle())) { - int err = errno; - throw std::runtime_error("Error making notification channel pipe non-blocking: " + stringerror(err)); - } - - if (pipeBufferSize > 0 && getPipeBufferSize(receiver.getHandle()) < pipeBufferSize) { - setPipeBufferSize(receiver.getHandle(), pipeBufferSize); - } - - return std::pair(Notifier(std::move(sender)), Waiter(std::move(receiver))); - } -} -} diff --git a/pdns/dnsdistdist/channel.cc b/pdns/dnsdistdist/channel.cc new file mode 120000 index 0000000000..54617106c4 --- /dev/null +++ b/pdns/dnsdistdist/channel.cc @@ -0,0 +1 @@ +../channel.cc \ No newline at end of file diff --git a/pdns/dnsdistdist/channel.hh b/pdns/dnsdistdist/channel.hh deleted file mode 100644 index 5c352870ed..0000000000 --- a/pdns/dnsdistdist/channel.hh +++ /dev/null @@ -1,285 +0,0 @@ -/* - * This file is part of PowerDNS or dnsdist. - * Copyright -- PowerDNS.COM B.V. and its contributors - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * In addition, for the avoidance of any doubt, permission is granted to - * link this program with OpenSSL and to (re)distribute the binaries - * produced as the result of such linking. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -#pragma once -#include -#include - -#include "misc.hh" - -namespace pdns -{ -namespace channel -{ - /** - * The sender's end of a channel used to pass objects between threads. - * - * A sender can be used by several threads in a safe way. - */ - template > - class Sender - { - public: - Sender() - { - } - Sender(FDWrapper&& fd) : - d_fd(std::move(fd)) - { - } - Sender(const Sender&) = delete; - Sender& operator=(const Sender&) = delete; - Sender(Sender&&) = default; - Sender& operator=(Sender&&) = default; - /** - * \brief Try to send the supplied object to the other end of that channel. Might block if the channel was created in blocking mode. - * - * \return True if the object was properly sent, False if the channel is full. - * - * \throw runtime_error if the channel is broken, for example if the other end has been closed. - */ - bool send(std::unique_ptr&&) const; - - private: - FDWrapper d_fd; - }; - - /** - * The receiver's end of a channel used to pass objects between threads. - * - * A receiver can be used by several threads in a safe way, but in that case spurious wake up might happen. - */ - template > - class Receiver - { - public: - Receiver() - { - } - Receiver(FDWrapper&& fd) : - d_fd(std::move(fd)) - { - } - Receiver(const Receiver&) = delete; - Receiver& operator=(const Receiver&) = delete; - Receiver(Receiver&&) = default; - Receiver& operator=(Receiver&&) = default; - /** - * \brief Try to read an object sent by the other end of that channel. Might block if the channel was created in blocking mode. - * - * \return An object if one was available, and std::nullopt otherwise. - * - * \throw runtime_error if the channel is broken, for example if the other end has been closed. - */ - std::optional> receive() const; - std::optional> receive(D deleter) const; - - /** - * \brief Get a descriptor that can be used with an I/O multiplexer to wait for an object to become available. - * - * \return A valid descriptor or -1 if the Receiver was not properly initialized. - */ - int getDescriptor() const - { - return d_fd.getHandle(); - } - - private: - FDWrapper d_fd; - }; - - /** - * \brief Create a channel to pass objects between threads, accepting multiple senders and receivers. - * - * \return A pair of Sender and Receiver objects. - * - * \throw runtime_error if the channel creation failed. - */ - template > - std::pair, Receiver> createObjectQueue(bool nonBlocking = true, size_t pipeBufferSize = 0); - - /** - * The notifier's end of a channel used to communicate between threads. - * - * A notifier can be used by several threads in a safe way. - */ - class Notifier - { - public: - Notifier() - { - } - Notifier(FDWrapper&&); - Notifier(const Notifier&) = delete; - Notifier& operator=(const Notifier&) = delete; - Notifier(Notifier&&) = default; - Notifier& operator=(Notifier&&) = default; - - /** - * \brief Queue a notification to wake up the other end of the channel. - * - * \return True if the notification was properly sent, False if the channel is full. - * - * \throw runtime_error if the channel is broken, for example if the other end has been closed. - */ - bool notify() const; - - private: - FDWrapper d_fd; - }; - - /** - * The waiter's end of a channel used to communicate between threads. - * - * A waiter can be used by several threads in a safe way, but in that case spurious wake up might happen. - */ - class Waiter - { - public: - Waiter(FDWrapper&&); - Waiter(const Waiter&) = delete; - Waiter& operator=(const Waiter&) = delete; - Waiter(Waiter&&) = default; - Waiter& operator=(Waiter&&) = default; - - /** - * \brief Clear all notifications queued on that channel, if any. - */ - void clear() const; - /** - * \brief Get a descriptor that can be used with an I/O multiplexer to wait for a notification to arrive. - * - * \return A valid descriptor or -1 if the Waiter was not properly initialized. - */ - int getDescriptor() const; - - private: - FDWrapper d_fd; - }; - - /** - * \brief Create a channel to notify one thread from another one, accepting multiple senders and receivers. - * - * \return A pair of Notifier and Sender objects. - * - * \throw runtime_error if the channel creation failed. - */ - std::pair createNotificationQueue(bool nonBlocking = true, size_t pipeBufferSize = 0); - - template - bool Sender::send(std::unique_ptr&& object) const - { - // we do not release right away because we might need the custom deleter later - auto ptr = object.get(); - static_assert(sizeof(ptr) <= PIPE_BUF, "Writes up to PIPE_BUF are guaranted not to interleaved and to either fully succeed or fail"); - ssize_t sent = write(d_fd.getHandle(), &ptr, sizeof(ptr)); - - if (sent == sizeof(ptr)) { - // we cannot touch it anymore - object.release(); - } - else { - if (errno == EAGAIN || errno == EWOULDBLOCK) { - return false; - } - else { - throw std::runtime_error("Unable to write to channel:" + stringerror()); - } - } - - return true; - } - - template - std::optional> Receiver::receive() const - { - std::optional> result; - T* obj{nullptr}; - ssize_t got = read(d_fd.getHandle(), &obj, sizeof(obj)); - if (got == sizeof(obj)) { - return std::unique_ptr(obj); - } - else if (got == 0) { - throw std::runtime_error("EOF while reading from Channel receiver"); - } - else if (got == -1) { - if (errno == EAGAIN || errno == EINTR) { - return result; - } - throw std::runtime_error("Error while reading from Channel receiver: " + stringerror()); - } - else { - throw std::runtime_error("Partial read from Channel receiver"); - } - } - - template - std::optional> Receiver::receive(D deleter) const - { - std::optional> result; - T* obj{nullptr}; - ssize_t got = read(d_fd.getHandle(), &obj, sizeof(obj)); - if (got == sizeof(obj)) { - return std::unique_ptr(obj, deleter); - } - else if (got == 0) { - throw std::runtime_error("EOF while reading from Channel receiver"); - } - else if (got == -1) { - if (errno == EAGAIN || errno == EINTR) { - return result; - } - throw std::runtime_error("Error while reading from Channel receiver: " + stringerror()); - } - else { - throw std::runtime_error("Partial read from Channel receiver"); - } - } - - template - std::pair, Receiver> createObjectQueue(bool nonBlocking, size_t pipeBufferSize) - { - int fds[2] = {-1, -1}; - if (pipe(fds) < 0) { - throw std::runtime_error("Error creating channel pipe: " + stringerror()); - } - - FDWrapper sender(fds[1]); - FDWrapper receiver(fds[0]); - - if (nonBlocking && !setNonBlocking(receiver.getHandle())) { - int err = errno; - throw std::runtime_error("Error making channel pipe non-blocking: " + stringerror(err)); - } - - if (nonBlocking && !setNonBlocking(sender.getHandle())) { - int err = errno; - throw std::runtime_error("Error making channel pipe non-blocking: " + stringerror(err)); - } - - if (pipeBufferSize > 0 && getPipeBufferSize(receiver.getHandle()) < pipeBufferSize) { - setPipeBufferSize(receiver.getHandle(), pipeBufferSize); - } - - return std::pair(Sender(std::move(sender)), Receiver(std::move(receiver))); - } -} -} diff --git a/pdns/dnsdistdist/channel.hh b/pdns/dnsdistdist/channel.hh new file mode 120000 index 0000000000..799a313ab8 --- /dev/null +++ b/pdns/dnsdistdist/channel.hh @@ -0,0 +1 @@ +../channel.hh \ No newline at end of file