]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Move channel files from pdns/dnsdistdist to pdns
authorRemi Gacogne <remi.gacogne@powerdns.com>
Fri, 10 Jun 2022 15:34:56 +0000 (17:34 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 13 Jun 2023 07:59:37 +0000 (09:59 +0200)
pdns/Makefile.am
pdns/channel.cc [new file with mode: 0644]
pdns/channel.hh [new file with mode: 0644]
pdns/dnsdistdist/channel.cc [changed from file to symlink]
pdns/dnsdistdist/channel.hh [changed from file to symlink]

index 88229c32a7416c682027be9dbfd8558352037c2d..f9e8c632cd24c14fff31d522578da1219447eba4 100644 (file)
@@ -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 (file)
index 0000000..dd25c6d
--- /dev/null
@@ -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<Notifier, Waiter> 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 (file)
index 0000000..5c35287
--- /dev/null
@@ -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 <memory>
+#include <optional>
+
+#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 <typename T, typename D = std::default_delete<T>>
+  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<T, D>&&) 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 <typename T, typename D = std::default_delete<T>>
+  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<std::unique_ptr<T, D>> receive() const;
+    std::optional<std::unique_ptr<T, D>> 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 <typename T, typename D = std::default_delete<T>>
+  std::pair<Sender<T, D>, Receiver<T, D>> 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<Notifier, Waiter> createNotificationQueue(bool nonBlocking = true, size_t pipeBufferSize = 0);
+
+  template <typename T, typename D>
+  bool Sender<T, D>::send(std::unique_ptr<T, D>&& 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 <typename T, typename D>
+  std::optional<std::unique_ptr<T, D>> Receiver<T, D>::receive() const
+  {
+    std::optional<std::unique_ptr<T, D>> result;
+    T* obj{nullptr};
+    ssize_t got = read(d_fd.getHandle(), &obj, sizeof(obj));
+    if (got == sizeof(obj)) {
+      return std::unique_ptr<T, D>(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 <typename T, typename D>
+  std::optional<std::unique_ptr<T, D>> Receiver<T, D>::receive(D deleter) const
+  {
+    std::optional<std::unique_ptr<T, D>> result;
+    T* obj{nullptr};
+    ssize_t got = read(d_fd.getHandle(), &obj, sizeof(obj));
+    if (got == sizeof(obj)) {
+      return std::unique_ptr<T, D>(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 <typename T, typename D>
+  std::pair<Sender<T, D>, Receiver<T, D>> 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<T, D>(std::move(sender)), Receiver<T, D>(std::move(receiver)));
+  }
+}
+}
deleted file mode 100644 (file)
index dd25c6d34eeef1a21502659733cb23fa134292a6..0000000000000000000000000000000000000000
+++ /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<Notifier, Waiter> 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)));
-  }
-}
-}
new file mode 120000 (symlink)
index 0000000000000000000000000000000000000000..54617106c4846e2a38e98071b3b97334c9ff2588
--- /dev/null
@@ -0,0 +1 @@
+../channel.cc
\ No newline at end of file
deleted file mode 100644 (file)
index 5c352870ed6051363ad53d86432833b0707bff66..0000000000000000000000000000000000000000
+++ /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 <memory>
-#include <optional>
-
-#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 <typename T, typename D = std::default_delete<T>>
-  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<T, D>&&) 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 <typename T, typename D = std::default_delete<T>>
-  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<std::unique_ptr<T, D>> receive() const;
-    std::optional<std::unique_ptr<T, D>> 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 <typename T, typename D = std::default_delete<T>>
-  std::pair<Sender<T, D>, Receiver<T, D>> 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<Notifier, Waiter> createNotificationQueue(bool nonBlocking = true, size_t pipeBufferSize = 0);
-
-  template <typename T, typename D>
-  bool Sender<T, D>::send(std::unique_ptr<T, D>&& 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 <typename T, typename D>
-  std::optional<std::unique_ptr<T, D>> Receiver<T, D>::receive() const
-  {
-    std::optional<std::unique_ptr<T, D>> result;
-    T* obj{nullptr};
-    ssize_t got = read(d_fd.getHandle(), &obj, sizeof(obj));
-    if (got == sizeof(obj)) {
-      return std::unique_ptr<T, D>(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 <typename T, typename D>
-  std::optional<std::unique_ptr<T, D>> Receiver<T, D>::receive(D deleter) const
-  {
-    std::optional<std::unique_ptr<T, D>> result;
-    T* obj{nullptr};
-    ssize_t got = read(d_fd.getHandle(), &obj, sizeof(obj));
-    if (got == sizeof(obj)) {
-      return std::unique_ptr<T, D>(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 <typename T, typename D>
-  std::pair<Sender<T, D>, Receiver<T, D>> 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<T, D>(std::move(sender)), Receiver<T, D>(std::move(receiver)));
-  }
-}
-}
new file mode 120000 (symlink)
index 0000000000000000000000000000000000000000..799a313ab82e67979c59449ada5128d9e38d5faf
--- /dev/null
@@ -0,0 +1 @@
+../channel.hh
\ No newline at end of file