From a3abb85c6ff5aebe2220ae612512a434b491eedd Mon Sep 17 00:00:00 2001
From: Justin Berger <j.david.berger@gmail.com>
Date: Thu, 27 Jul 2017 07:11:07 -0600
Subject: Add RAII handles for libuv handle types

The `uv_*_t` handle types are closed by `uv_close`, but the semantics
are tricky.  Calling `uv_close` may not close immediately.  Instead it
hands ownership to the uv loop to which the handle is currently
attached.  When the loop decides to close it, a callback is used to
allow the `uv_close` caller to free resources.

Provide an abstraction layer as `cm::uv_*_ptr` types corresponding to
the `uv_*_t` handle types.  Each pointer is either empty (`nullptr`)
or has an initialized handle attached to a loop.  Use move semantics
to ensure a single owner of the handle so that clients can predict
when the handle is destroyed.
---
 Source/CMakeLists.txt    |   1 +
 Source/cmUVHandlePtr.cxx | 198 ++++++++++++++++++++++++++++++++++++++++++++++
 Source/cmUVHandlePtr.h   | 200 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 399 insertions(+)
 create mode 100644 Source/cmUVHandlePtr.cxx
 create mode 100644 Source/cmUVHandlePtr.h

diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 33ab093..88c63e1 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -1029,6 +1029,7 @@ list(APPEND _tools cmake)
 target_link_libraries(cmake CMakeLib)
 
 add_library(CMakeServerLib
+  cmUVHandlePtr.h cmUVHandlePtr.cxx
   cmConnection.h cmConnection.cxx
   cmFileMonitor.cxx cmFileMonitor.h
   cmPipeConnection.cxx cmPipeConnection.h
diff --git a/Source/cmUVHandlePtr.cxx b/Source/cmUVHandlePtr.cxx
new file mode 100644
index 0000000..214c7b2
--- /dev/null
+++ b/Source/cmUVHandlePtr.cxx
@@ -0,0 +1,198 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#define cmUVHandlePtr_cxx
+#include "cmUVHandlePtr.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "cm_thread.hxx"
+#include "cm_uv.h"
+
+namespace cm {
+
+static void close_delete(uv_handle_t* h)
+{
+  free(h);
+}
+
+template <typename T>
+static void default_delete(T* type_handle)
+{
+  auto handle = reinterpret_cast<uv_handle_t*>(type_handle);
+  if (handle) {
+    assert(!uv_is_closing(handle));
+    if (!uv_is_closing(handle)) {
+      uv_close(handle, &close_delete);
+    }
+  }
+}
+
+/**
+ * Encapsulates delete logic for a given handle type T
+ */
+template <typename T>
+struct uv_handle_deleter
+{
+  void operator()(T* type_handle) const { default_delete(type_handle); }
+};
+
+template <typename T>
+void uv_handle_ptr_base_<T>::allocate(void* data)
+{
+  reset();
+
+  /*
+    We use calloc since we know all these types are c structs
+    and we just want to 0 init them. New would do the same thing;
+    but casting from uv_handle_t to certain other types -- namely
+    uv_timer_t -- triggers a cast_align warning on certain systems.
+  */
+  handle.reset(static_cast<T*>(calloc(1, sizeof(T))), uv_handle_deleter<T>());
+  handle->data = data;
+}
+
+template <typename T>
+void uv_handle_ptr_base_<T>::reset()
+{
+  handle.reset();
+}
+
+template <typename T>
+uv_handle_ptr_base_<T>::operator uv_handle_t*()
+{
+  return reinterpret_cast<uv_handle_t*>(handle.get());
+}
+
+template <typename T>
+T* uv_handle_ptr_base_<T>::operator->() const noexcept
+{
+  return handle.get();
+}
+
+template <typename T>
+T* uv_handle_ptr_base_<T>::get() const
+{
+  return handle.get();
+}
+
+template <typename T>
+uv_handle_ptr_<T>::operator T*() const
+{
+  return this->handle.get();
+}
+
+template <>
+struct uv_handle_deleter<uv_async_t>
+{
+  /***
+  * Wile uv_async_send is itself thread-safe, there are
+  * no strong guarantees that close hasn't already been
+  * called on the handle; and that it might be deleted
+  * as the send call goes through. This mutex guards
+  * against that.
+  *
+  * The shared_ptr here is to allow for copy construction
+  * which is mandated by the standard for Deleter on
+  * shared_ptrs.
+  */
+  std::shared_ptr<cm::mutex> handleMutex;
+
+  uv_handle_deleter()
+    : handleMutex(std::make_shared<cm::mutex>())
+  {
+  }
+
+  void operator()(uv_async_t* handle)
+  {
+    cm::lock_guard<cm::mutex> lock(*handleMutex);
+    default_delete(handle);
+  }
+};
+
+void uv_async_ptr::send()
+{
+  auto deleter = std::get_deleter<uv_handle_deleter<uv_async_t>>(this->handle);
+  assert(deleter);
+
+  cm::lock_guard<cm::mutex> lock(*deleter->handleMutex);
+  if (this->handle) {
+    uv_async_send(*this);
+  }
+}
+
+int uv_async_ptr::init(uv_loop_t& loop, uv_async_cb async_cb, void* data)
+{
+  allocate(data);
+  return uv_async_init(&loop, handle.get(), async_cb);
+}
+
+template <>
+struct uv_handle_deleter<uv_signal_t>
+{
+  void operator()(uv_signal_t* handle) const
+  {
+    if (handle) {
+      uv_signal_stop(handle);
+      default_delete(handle);
+    }
+  }
+};
+
+int uv_signal_ptr::init(uv_loop_t& loop, void* data)
+{
+  allocate(data);
+  return uv_signal_init(&loop, handle.get());
+}
+
+int uv_signal_ptr::start(uv_signal_cb cb, int signum)
+{
+  assert(handle);
+  return uv_signal_start(*this, cb, signum);
+}
+
+void uv_signal_ptr::stop()
+{
+  if (handle) {
+    uv_signal_stop(*this);
+  }
+}
+
+int uv_pipe_ptr::init(uv_loop_t& loop, int ipc, void* data)
+{
+  allocate(data);
+  return uv_pipe_init(&loop, *this, ipc);
+}
+
+uv_pipe_ptr::operator uv_stream_t*() const
+{
+  return reinterpret_cast<uv_stream_t*>(handle.get());
+}
+
+uv_tty_ptr::operator uv_stream_t*() const
+{
+  return reinterpret_cast<uv_stream_t*>(handle.get());
+}
+
+int uv_tty_ptr::init(uv_loop_t& loop, int fd, int readable, void* data)
+{
+  allocate(data);
+  return uv_tty_init(&loop, *this, fd, readable);
+}
+
+template class uv_handle_ptr_base_<uv_handle_t>;
+
+#define UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(NAME)                              \
+  template class uv_handle_ptr_base_<uv_##NAME##_t>;                          \
+  template class uv_handle_ptr_<uv_##NAME##_t>;
+
+UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(async)
+
+UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(signal)
+
+UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(pipe)
+
+UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(stream)
+
+UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(tty)
+}
diff --git a/Source/cmUVHandlePtr.h b/Source/cmUVHandlePtr.h
new file mode 100644
index 0000000..a251c5f
--- /dev/null
+++ b/Source/cmUVHandlePtr.h
@@ -0,0 +1,200 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <algorithm>
+#include <cstddef>
+#include <memory>
+#include <type_traits>
+
+#include "cm_uv.h"
+
+#define CM_PERFECT_FWD_CTOR(Class, FwdTo)                                     \
+  template <typename... Args>                                                 \
+  Class(Args&&... args)                                                       \
+    : FwdTo(std::forward<Args>(args)...)                                      \
+  {                                                                           \
+  }
+
+namespace cm {
+
+/***
+* RAII class to simplify and insure the safe usage of uv_*_t types. This
+* includes making sure resources are properly freed and contains casting
+* operators which allow for passing into relevant uv_* functions.
+*
+*@tparam T actual uv_*_t type represented.
+*/
+template <typename T>
+class uv_handle_ptr_base_
+{
+protected:
+  template <typename _T>
+  friend class uv_handle_ptr_base_;
+
+  /**
+   * This must be a pointer type since the handle can outlive this class.
+   * When uv_close is eventually called on the handle, the memory the
+   * handle inhabits must be valid until the close callback is called
+   * which can be later on in the loop.
+   */
+  std::shared_ptr<T> handle;
+
+  /**
+   * Allocate memory for the type and optionally set it's 'data' pointer.
+   * Protected since this should only be called for an appropriate 'init'
+   * call.
+   *
+   * @param data data pointer to set
+   */
+  void allocate(void* data = nullptr);
+
+public:
+  CM_DISABLE_COPY(uv_handle_ptr_base_)
+  uv_handle_ptr_base_(uv_handle_ptr_base_&&) noexcept;
+  uv_handle_ptr_base_& operator=(uv_handle_ptr_base_&&) noexcept;
+
+  /**
+   * This move constructor allows us to move out of a more specialized
+   * uv type into a less specialized one. The only constraint is that
+   * the right hand side is castable to T.
+   *
+   * This allows you to return uv_handle_ptr or uv_stream_ptr from a function
+   * that initializes something like uv_pipe_ptr or uv_tcp_ptr and interact
+   * and clean up after it without caring about the exact type.
+   */
+  template <typename S, typename = typename std::enable_if<
+                          std::is_rvalue_reference<S&&>::value>::type>
+  uv_handle_ptr_base_(S&& rhs)
+  {
+    // This will force a compiler error if rhs doesn't have a casting
+    // operator to get T*
+    this->handle = std::shared_ptr<T>(rhs.handle, rhs);
+    rhs.handle.reset();
+  }
+
+  // Dtor and ctor need to be inline defined like this for default ctors and
+  // dtors to work.
+  uv_handle_ptr_base_() {}
+  uv_handle_ptr_base_(std::nullptr_t) {}
+  ~uv_handle_ptr_base_() { reset(); }
+
+  /**
+   * Properly close the handle if needed and sets the inner handle to nullptr
+   */
+  void reset();
+
+  /**
+   * Allow less verbose calling of uv_handle_* functions
+   * @return reinterpreted handle
+   */
+  operator uv_handle_t*();
+
+  T* get() const;
+  T* operator->() const noexcept;
+};
+
+template <typename T>
+inline uv_handle_ptr_base_<T>::uv_handle_ptr_base_(
+  uv_handle_ptr_base_<T>&&) noexcept = default;
+template <typename T>
+inline uv_handle_ptr_base_<T>& uv_handle_ptr_base_<T>::operator=(
+  uv_handle_ptr_base_<T>&&) noexcept = default;
+
+/**
+ * While uv_handle_ptr_base_ only exposes uv_handle_t*, this exposes uv_T_t*
+ * too. It is broken out like this so we can reuse most of the code for the
+ * uv_handle_ptr class.
+ */
+template <typename T>
+class uv_handle_ptr_ : public uv_handle_ptr_base_<T>
+{
+  template <typename _T>
+  friend class uv_handle_ptr_;
+
+public:
+  CM_PERFECT_FWD_CTOR(uv_handle_ptr_, uv_handle_ptr_base_<T>);
+
+  /***
+   * Allow less verbose calling of uv_<T> functions
+   * @return reinterpreted handle
+   */
+  operator T*() const;
+};
+
+/***
+ * This specialization is required to avoid duplicate 'operator uv_handle_t*()'
+ * declarations
+ */
+template <>
+class uv_handle_ptr_<uv_handle_t> : public uv_handle_ptr_base_<uv_handle_t>
+{
+public:
+  CM_PERFECT_FWD_CTOR(uv_handle_ptr_, uv_handle_ptr_base_<uv_handle_t>);
+};
+
+class uv_async_ptr : public uv_handle_ptr_<uv_async_t>
+{
+public:
+  CM_PERFECT_FWD_CTOR(uv_async_ptr, uv_handle_ptr_<uv_async_t>);
+
+  int init(uv_loop_t& loop, uv_async_cb async_cb, void* data = nullptr);
+
+  void send();
+};
+
+struct uv_signal_ptr : public uv_handle_ptr_<uv_signal_t>
+{
+  CM_PERFECT_FWD_CTOR(uv_signal_ptr, uv_handle_ptr_<uv_signal_t>);
+
+  int init(uv_loop_t& loop, void* data = nullptr);
+
+  int start(uv_signal_cb cb, int signum);
+
+  void stop();
+};
+
+struct uv_pipe_ptr : public uv_handle_ptr_<uv_pipe_t>
+{
+  CM_PERFECT_FWD_CTOR(uv_pipe_ptr, uv_handle_ptr_<uv_pipe_t>);
+
+  operator uv_stream_t*() const;
+
+  int init(uv_loop_t& loop, int ipc, void* data = nullptr);
+};
+
+struct uv_tty_ptr : public uv_handle_ptr_<uv_tty_t>
+{
+  CM_PERFECT_FWD_CTOR(uv_tty_ptr, uv_handle_ptr_<uv_tty_t>);
+
+  operator uv_stream_t*() const;
+
+  int init(uv_loop_t& loop, int fd, int readable, void* data = nullptr);
+};
+
+typedef uv_handle_ptr_<uv_stream_t> uv_stream_ptr;
+typedef uv_handle_ptr_<uv_handle_t> uv_handle_ptr;
+
+#ifndef cmUVHandlePtr_cxx
+
+extern template class uv_handle_ptr_base_<uv_handle_t>;
+
+#define UV_HANDLE_PTR_INSTANTIATE_EXTERN(NAME)                                \
+  extern template class uv_handle_ptr_base_<uv_##NAME##_t>;                   \
+  extern template class uv_handle_ptr_<uv_##NAME##_t>;
+
+UV_HANDLE_PTR_INSTANTIATE_EXTERN(async)
+
+UV_HANDLE_PTR_INSTANTIATE_EXTERN(signal)
+
+UV_HANDLE_PTR_INSTANTIATE_EXTERN(pipe)
+
+UV_HANDLE_PTR_INSTANTIATE_EXTERN(stream)
+
+UV_HANDLE_PTR_INSTANTIATE_EXTERN(tty)
+
+#undef UV_HANDLE_PTR_INSTANTIATE_EXTERN
+
+#endif
+}
-- 
cgit v0.12