/* 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 #include #include #include #include #include #if defined(__SUNPRO_CC) # include # define CM_INHERIT_CTOR(Class, Base, Tpl) \ template \ Class(Args&&... args) \ : Base Tpl(std::forward(args)...) \ { \ } #else # define CM_INHERIT_CTOR(Class, Base, Tpl) using Base Tpl ::Base #endif namespace cm { /*** * RAII class to simplify and ensure the safe usage of uv_loop_t. This includes * making sure resources are properly freed. */ class uv_loop_ptr { protected: std::shared_ptr loop; public: uv_loop_ptr(uv_loop_ptr const&) = delete; uv_loop_ptr& operator=(uv_loop_ptr const&) = delete; uv_loop_ptr(uv_loop_ptr&&) noexcept; uv_loop_ptr& operator=(uv_loop_ptr&&) noexcept; // Dtor and ctor need to be inline defined like this for default ctors and // dtors to work. Some compilers do not like '= default' here. uv_loop_ptr() {} // NOLINT(modernize-use-equals-default) uv_loop_ptr(std::nullptr_t) {} ~uv_loop_ptr() { this->reset(); } int init(void* data = nullptr); /** * Properly close the handle if needed and sets the inner handle to nullptr */ void reset(); /** * Allow less verbose calling of uv_loop_* functions * @return reinterpreted handle */ operator uv_loop_t*() const; uv_loop_t* get() const; uv_loop_t* operator->() const noexcept; uv_loop_t& operator*() const; }; /*** * RAII class to simplify and ensure 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 class uv_handle_ptr_base_ { protected: template 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 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: uv_handle_ptr_base_(uv_handle_ptr_base_ const&) = delete; uv_handle_ptr_base_& operator=(uv_handle_ptr_base_ const&) = delete; 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 ::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(rhs.handle, rhs); rhs.handle.reset(); } // Dtor and ctor need to be inline defined like this for default ctors and // dtors to work. Some compilers do not like '= default' here. uv_handle_ptr_base_() {} // NOLINT(modernize-use-equals-default) uv_handle_ptr_base_(std::nullptr_t) {} ~uv_handle_ptr_base_() { this->reset(); } #if defined(__SUNPRO_CC) // The Oracle Studio compiler recognizes 'explicit operator bool()' in // 'if(foo)' but not 'if(foo && ...)'. The purpose of 'explicit' here // is to avoid accidental conversion in non-boolean contexts. Just // leave it out on this compiler so we can compile valid code. operator bool() const; #else explicit operator bool() const; #endif /** * 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*() const; T* get() const; T* operator->() const noexcept; }; template inline uv_handle_ptr_base_::uv_handle_ptr_base_( uv_handle_ptr_base_&&) noexcept = default; template inline uv_handle_ptr_base_& uv_handle_ptr_base_::operator=( uv_handle_ptr_base_&&) 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 class uv_handle_ptr_ : public uv_handle_ptr_base_ { template friend class uv_handle_ptr_; public: CM_INHERIT_CTOR(uv_handle_ptr_, uv_handle_ptr_base_, ); /*** * Allow less verbose calling of uv_ functions * @return reinterpreted handle */ operator T*() const; }; /*** * This specialization is required to avoid duplicate 'operator uv_handle_t*()' * declarations */ template <> class uv_handle_ptr_ : public uv_handle_ptr_base_ { public: CM_INHERIT_CTOR(uv_handle_ptr_, uv_handle_ptr_base_, ); }; class uv_async_ptr : public uv_handle_ptr_ { public: CM_INHERIT_CTOR(uv_async_ptr, uv_handle_ptr_, ); int init(uv_loop_t& loop, uv_async_cb async_cb, void* data = nullptr); void send(); }; struct uv_idle_ptr : public uv_handle_ptr_ { CM_INHERIT_CTOR(uv_idle_ptr, uv_handle_ptr_, ); int init(uv_loop_t& loop, void* data = nullptr); int start(uv_idle_cb cb); void stop(); }; struct uv_signal_ptr : public uv_handle_ptr_ { CM_INHERIT_CTOR(uv_signal_ptr, uv_handle_ptr_, ); 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_ { CM_INHERIT_CTOR(uv_pipe_ptr, uv_handle_ptr_, ); operator uv_stream_t*() const; int init(uv_loop_t& loop, int ipc, void* data = nullptr); }; struct uv_process_ptr : public uv_handle_ptr_ { CM_INHERIT_CTOR(uv_process_ptr, uv_handle_ptr_, ); int spawn(uv_loop_t& loop, uv_process_options_t const& options, void* data = nullptr); }; struct uv_timer_ptr : public uv_handle_ptr_ { CM_INHERIT_CTOR(uv_timer_ptr, uv_handle_ptr_, ); int init(uv_loop_t& loop, void* data = nullptr); int start(uv_timer_cb cb, uint64_t timeout, uint64_t repeat); void stop(); }; struct uv_tty_ptr : public uv_handle_ptr_ { CM_INHERIT_CTOR(uv_tty_ptr, uv_handle_ptr_, ); operator uv_stream_t*() const; int init(uv_loop_t& loop, int fd, int readable, void* data = nullptr); }; using uv_stream_ptr = uv_handle_ptr_; using uv_handle_ptr = uv_handle_ptr_; #ifndef cmUVHandlePtr_cxx extern template class uv_handle_ptr_base_; # define UV_HANDLE_PTR_INSTANTIATE_EXTERN(NAME) \ extern template class uv_handle_ptr_base_; \ extern template class uv_handle_ptr_; UV_HANDLE_PTR_INSTANTIATE_EXTERN(async) UV_HANDLE_PTR_INSTANTIATE_EXTERN(idle) 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 /** * Wraps uv_write to add synchronous cancellation. * * libuv provides no way to synchronously cancel a write request. * Closing a write handle will cancel its pending write request, but its * callback will still be called asynchronously later with UV_ECANCELED. * * This wrapper provides a solution by handing ownership of the uv_write_t * request object to the event loop and taking it back in the callback. * Use this in combination with uv_loop_ptr to ensure the event loop * runs to completion and cleans up all resources. * * The caller may optionally provide a callback it owns with std::shared_ptr. * If the caller's lifetime ends before the write request completes, the * callback can be safely deleted and will not be called. * * The bufs array does not need to live beyond this call, but the memory * referenced by the uv_buf_t values must remain alive until the callback * is made or the stream is closed. */ int uv_write(uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, std::weak_ptr> cb); }