1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
|
/* 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);
}
|