summaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
authorJustin Berger <j.david.berger@gmail.com>2017-07-27 13:11:07 (GMT)
committerBrad King <brad.king@kitware.com>2017-11-29 21:36:31 (GMT)
commita3abb85c6ff5aebe2220ae612512a434b491eedd (patch)
tree155409d97d1bca65c774c999c56e1f171ab0a3ad /Source
parent90f8db269fcc6978badd93b5f985dee2e01feab3 (diff)
downloadCMake-a3abb85c6ff5aebe2220ae612512a434b491eedd.zip
CMake-a3abb85c6ff5aebe2220ae612512a434b491eedd.tar.gz
CMake-a3abb85c6ff5aebe2220ae612512a434b491eedd.tar.bz2
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.
Diffstat (limited to 'Source')
-rw-r--r--Source/CMakeLists.txt1
-rw-r--r--Source/cmUVHandlePtr.cxx198
-rw-r--r--Source/cmUVHandlePtr.h200
3 files changed, 399 insertions, 0 deletions
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
+}