/* 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 <cstdint> #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_process_ptr : public uv_handle_ptr_<uv_process_t> { CM_PERFECT_FWD_CTOR(uv_process_ptr, uv_handle_ptr_<uv_process_t>); int spawn(uv_loop_t& loop, uv_process_options_t const& options, void* data = nullptr); }; struct uv_timer_ptr : public uv_handle_ptr_<uv_timer_t> { CM_PERFECT_FWD_CTOR(uv_timer_ptr, uv_handle_ptr_<uv_timer_t>); int init(uv_loop_t& loop, void* data = nullptr); int start(uv_timer_cb cb, uint64_t timeout, uint64_t repeat); }; 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(process) UV_HANDLE_PTR_INSTANTIATE_EXTERN(stream) UV_HANDLE_PTR_INSTANTIATE_EXTERN(timer) UV_HANDLE_PTR_INSTANTIATE_EXTERN(tty) # undef UV_HANDLE_PTR_INSTANTIATE_EXTERN #endif }