|
|
|
/* 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 <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 {
|
|
|
|
|
|
|
|
/***
|
|
|
|
* 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>
|
|
|
|
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 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);
|
|
|
|
|
|
|
|
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, const uv_buf_t bufs[], unsigned int nbufs,
|
|
|
|
std::weak_ptr<std::function<void(int)>> cb);
|
|
|
|
}
|