| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #pragma once | |
| #include "cmConfigure.h" // IWYU pragma: keep | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <functional> | |
| #include <memory> | |
| #include <type_traits> | |
| #include <cm3p/uv.h> | |
| #if defined(__SUNPRO_CC) | |
| # include <utility> | |
| # define CM_INHERIT_CTOR(Class, Base, Tpl) \ | |
| template <typename... Args> \ | |
| Class(Args&&... args) \ | |
| : Base Tpl(std::forward<Args>(args)...) \ | |
| { \ | |
| } | |
| #else | |
| # define CM_INHERIT_CTOR(Class, Base, Tpl) using Base Tpl ::Base | |
| #endif | |
| namespace cm { | |
| /** Whether to call uv_update_time before starting a timer. | |
| * | |
| * uv_loop_t caches a "now" time that uv_loop_init initializes and | |
| * uv_run updates on each event loop iteration. uv_timer_start | |
| * computes timer expiry relative to the loop's cached "now" time. | |
| * For short timeouts started before the event loop, we may need to | |
| * update the "now" time when starting the timer. | |
| */ | |
| enum class uv_update_time | |
| { | |
| no, | |
| yes, | |
| }; | |
| /*** | |
| * 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<uv_loop_t> 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 <typename T> | |
| class uv_handle_ptr_base_ | |
| { | |
| protected: | |
| template <typename U> | |
| 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: | |
| 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 <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. 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 <typename T> | |
| uv_handle_ptr_base_<T>::uv_handle_ptr_base_( | |
| uv_handle_ptr_base_<T>&&) noexcept = default; | |
| template <typename T> | |
| 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 U> | |
| friend class uv_handle_ptr_; | |
| public: | |
| CM_INHERIT_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_INHERIT_CTOR(uv_handle_ptr_, uv_handle_ptr_base_, <uv_handle_t>); | |
| }; | |
| class uv_async_ptr : public uv_handle_ptr_<uv_async_t> | |
| { | |
| public: | |
| CM_INHERIT_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_idle_ptr : public uv_handle_ptr_<uv_idle_t> | |
| { | |
| CM_INHERIT_CTOR(uv_idle_ptr, uv_handle_ptr_, <uv_idle_t>); | |
| 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_<uv_signal_t> | |
| { | |
| CM_INHERIT_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_INHERIT_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_INHERIT_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_INHERIT_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, | |
| uv_update_time update_time); | |
| void stop(); | |
| }; | |
| struct uv_tty_ptr : public uv_handle_ptr_<uv_tty_t> | |
| { | |
| CM_INHERIT_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); | |
| }; | |
| using uv_stream_ptr = uv_handle_ptr_<uv_stream_t>; | |
| using uv_handle_ptr = uv_handle_ptr_<uv_handle_t>; | |
| #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(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, uv_buf_t const bufs[], unsigned int nbufs, | |
| std::weak_ptr<std::function<void(int)>> cb); | |
| } |