Coverage Report

Created: 2026-03-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmUVHandlePtr.h
Line
Count
Source
1
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2
   file LICENSE.rst or https://cmake.org/licensing for details.  */
3
#pragma once
4
#include "cmConfigure.h" // IWYU pragma: keep
5
6
#include <cstddef>
7
#include <cstdint>
8
#include <functional>
9
#include <memory>
10
#include <type_traits>
11
12
#include <cm3p/uv.h>
13
14
#if defined(__SUNPRO_CC)
15
16
#  include <utility>
17
18
#  define CM_INHERIT_CTOR(Class, Base, Tpl)                                   \
19
    template <typename... Args>                                               \
20
    Class(Args&&... args)                                                     \
21
      : Base Tpl(std::forward<Args>(args)...)                                 \
22
    {                                                                         \
23
    }
24
25
#else
26
27
#  define CM_INHERIT_CTOR(Class, Base, Tpl) using Base Tpl ::Base
28
29
#endif
30
31
namespace cm {
32
33
/** Whether to call uv_update_time before starting a timer.
34
 *
35
 * uv_loop_t caches a "now" time that uv_loop_init initializes and
36
 * uv_run updates on each event loop iteration.  uv_timer_start
37
 * computes timer expiry relative to the loop's cached "now" time.
38
 * For short timeouts started before the event loop, we may need to
39
 * update the "now" time when starting the timer.
40
 */
41
enum class uv_update_time
42
{
43
  no,
44
  yes,
45
};
46
47
/***
48
 * RAII class to simplify and ensure the safe usage of uv_loop_t. This includes
49
 * making sure resources are properly freed.
50
 */
51
class uv_loop_ptr
52
{
53
protected:
54
  std::shared_ptr<uv_loop_t> loop;
55
56
public:
57
  uv_loop_ptr(uv_loop_ptr const&) = delete;
58
  uv_loop_ptr& operator=(uv_loop_ptr const&) = delete;
59
  uv_loop_ptr(uv_loop_ptr&&) noexcept;
60
  uv_loop_ptr& operator=(uv_loop_ptr&&) noexcept;
61
62
  // Dtor and ctor need to be inline defined like this for default ctors and
63
  // dtors to work.  Some compilers do not like '= default' here.
64
0
  uv_loop_ptr() {} // NOLINT(modernize-use-equals-default)
65
0
  uv_loop_ptr(std::nullptr_t) {}
66
0
  ~uv_loop_ptr() { this->reset(); }
67
68
  int init(void* data = nullptr);
69
70
  /**
71
   * Properly close the handle if needed and sets the inner handle to nullptr
72
   */
73
  void reset();
74
75
  /**
76
   * Allow less verbose calling of uv_loop_* functions
77
   * @return reinterpreted handle
78
   */
79
  operator uv_loop_t*() const;
80
81
  uv_loop_t* get() const;
82
  uv_loop_t* operator->() const noexcept;
83
  uv_loop_t& operator*() const;
84
};
85
86
/***
87
 * RAII class to simplify and ensure the safe usage of uv_*_t types. This
88
 * includes making sure resources are properly freed and contains casting
89
 * operators which allow for passing into relevant uv_* functions.
90
 *
91
 *@tparam T actual uv_*_t type represented.
92
 */
93
template <typename T>
94
class uv_handle_ptr_base_
95
{
96
protected:
97
  template <typename U>
98
  friend class uv_handle_ptr_base_;
99
100
  /**
101
   * This must be a pointer type since the handle can outlive this class.
102
   * When uv_close is eventually called on the handle, the memory the
103
   * handle inhabits must be valid until the close callback is called
104
   * which can be later on in the loop.
105
   */
106
  std::shared_ptr<T> handle;
107
108
  /**
109
   * Allocate memory for the type and optionally set it's 'data' pointer.
110
   * Protected since this should only be called for an appropriate 'init'
111
   * call.
112
   *
113
   * @param data data pointer to set
114
   */
115
  void allocate(void* data = nullptr);
116
117
public:
118
  uv_handle_ptr_base_(uv_handle_ptr_base_ const&) = delete;
119
  uv_handle_ptr_base_& operator=(uv_handle_ptr_base_ const&) = delete;
120
  uv_handle_ptr_base_(uv_handle_ptr_base_&&) noexcept;
121
  uv_handle_ptr_base_& operator=(uv_handle_ptr_base_&&) noexcept;
122
123
  /**
124
   * This move constructor allows us to move out of a more specialized
125
   * uv type into a less specialized one. The only constraint is that
126
   * the right hand side is castable to T.
127
   *
128
   * This allows you to return uv_handle_ptr or uv_stream_ptr from a function
129
   * that initializes something like uv_pipe_ptr or uv_tcp_ptr and interact
130
   * and clean up after it without caring about the exact type.
131
   */
132
  template <typename S,
133
            typename = typename std::enable_if<
134
              std::is_rvalue_reference<S&&>::value>::type>
135
  uv_handle_ptr_base_(S&& rhs)
136
  {
137
    // This will force a compiler error if rhs doesn't have a casting
138
    // operator to get T*
139
    this->handle = std::shared_ptr<T>(rhs.handle, rhs);
140
    rhs.handle.reset();
141
  }
142
143
  // Dtor and ctor need to be inline defined like this for default ctors and
144
  // dtors to work.  Some compilers do not like '= default' here.
145
0
  uv_handle_ptr_base_() {} // NOLINT(modernize-use-equals-default)
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_timer_s>::uv_handle_ptr_base_()
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_handle_s>::uv_handle_ptr_base_()
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_idle_s>::uv_handle_ptr_base_()
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_signal_s>::uv_handle_ptr_base_()
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_pipe_s>::uv_handle_ptr_base_()
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_stream_s>::uv_handle_ptr_base_()
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_process_s>::uv_handle_ptr_base_()
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_async_s>::uv_handle_ptr_base_()
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_tty_s>::uv_handle_ptr_base_()
146
0
  uv_handle_ptr_base_(std::nullptr_t) {}
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_handle_s>::uv_handle_ptr_base_(decltype(nullptr))
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_idle_s>::uv_handle_ptr_base_(decltype(nullptr))
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_signal_s>::uv_handle_ptr_base_(decltype(nullptr))
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_pipe_s>::uv_handle_ptr_base_(decltype(nullptr))
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_stream_s>::uv_handle_ptr_base_(decltype(nullptr))
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_process_s>::uv_handle_ptr_base_(decltype(nullptr))
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_timer_s>::uv_handle_ptr_base_(decltype(nullptr))
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_async_s>::uv_handle_ptr_base_(decltype(nullptr))
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_tty_s>::uv_handle_ptr_base_(decltype(nullptr))
147
0
  ~uv_handle_ptr_base_() { this->reset(); }
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_timer_s>::~uv_handle_ptr_base_()
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_handle_s>::~uv_handle_ptr_base_()
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_idle_s>::~uv_handle_ptr_base_()
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_signal_s>::~uv_handle_ptr_base_()
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_pipe_s>::~uv_handle_ptr_base_()
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_stream_s>::~uv_handle_ptr_base_()
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_process_s>::~uv_handle_ptr_base_()
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_async_s>::~uv_handle_ptr_base_()
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_tty_s>::~uv_handle_ptr_base_()
148
149
#if defined(__SUNPRO_CC)
150
  // The Oracle Studio compiler recognizes 'explicit operator bool()' in
151
  // 'if(foo)' but not 'if(foo && ...)'.  The purpose of 'explicit' here
152
  // is to avoid accidental conversion in non-boolean contexts.  Just
153
  // leave it out on this compiler so we can compile valid code.
154
  operator bool() const;
155
#else
156
  explicit operator bool() const;
157
#endif
158
159
  /**
160
   * Properly close the handle if needed and sets the inner handle to nullptr
161
   */
162
  void reset();
163
164
  /**
165
   * Allow less verbose calling of uv_handle_* functions
166
   * @return reinterpreted handle
167
   */
168
  operator uv_handle_t*() const;
169
170
  T* get() const;
171
  T* operator->() const noexcept;
172
};
173
174
template <typename T>
175
uv_handle_ptr_base_<T>::uv_handle_ptr_base_(
176
0
  uv_handle_ptr_base_<T>&&) noexcept = default;
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_handle_s>::uv_handle_ptr_base_(cm::uv_handle_ptr_base_<uv_handle_s>&&)
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_idle_s>::uv_handle_ptr_base_(cm::uv_handle_ptr_base_<uv_idle_s>&&)
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_signal_s>::uv_handle_ptr_base_(cm::uv_handle_ptr_base_<uv_signal_s>&&)
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_pipe_s>::uv_handle_ptr_base_(cm::uv_handle_ptr_base_<uv_pipe_s>&&)
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_stream_s>::uv_handle_ptr_base_(cm::uv_handle_ptr_base_<uv_stream_s>&&)
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_process_s>::uv_handle_ptr_base_(cm::uv_handle_ptr_base_<uv_process_s>&&)
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_timer_s>::uv_handle_ptr_base_(cm::uv_handle_ptr_base_<uv_timer_s>&&)
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_async_s>::uv_handle_ptr_base_(cm::uv_handle_ptr_base_<uv_async_s>&&)
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_tty_s>::uv_handle_ptr_base_(cm::uv_handle_ptr_base_<uv_tty_s>&&)
177
template <typename T>
178
uv_handle_ptr_base_<T>& uv_handle_ptr_base_<T>::operator=(
179
0
  uv_handle_ptr_base_<T>&&) noexcept = default;
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_handle_s>::operator=(cm::uv_handle_ptr_base_<uv_handle_s>&&)
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_idle_s>::operator=(cm::uv_handle_ptr_base_<uv_idle_s>&&)
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_signal_s>::operator=(cm::uv_handle_ptr_base_<uv_signal_s>&&)
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_pipe_s>::operator=(cm::uv_handle_ptr_base_<uv_pipe_s>&&)
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_stream_s>::operator=(cm::uv_handle_ptr_base_<uv_stream_s>&&)
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_process_s>::operator=(cm::uv_handle_ptr_base_<uv_process_s>&&)
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_timer_s>::operator=(cm::uv_handle_ptr_base_<uv_timer_s>&&)
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_async_s>::operator=(cm::uv_handle_ptr_base_<uv_async_s>&&)
Unexecuted instantiation: cm::uv_handle_ptr_base_<uv_tty_s>::operator=(cm::uv_handle_ptr_base_<uv_tty_s>&&)
180
181
/**
182
 * While uv_handle_ptr_base_ only exposes uv_handle_t*, this exposes uv_T_t*
183
 * too. It is broken out like this so we can reuse most of the code for the
184
 * uv_handle_ptr class.
185
 */
186
template <typename T>
187
class uv_handle_ptr_ : public uv_handle_ptr_base_<T>
188
{
189
  template <typename U>
190
  friend class uv_handle_ptr_;
191
192
public:
193
  CM_INHERIT_CTOR(uv_handle_ptr_, uv_handle_ptr_base_, <T>);
194
195
  /***
196
   * Allow less verbose calling of uv_<T> functions
197
   * @return reinterpreted handle
198
   */
199
  operator T*() const;
200
};
201
202
/***
203
 * This specialization is required to avoid duplicate 'operator uv_handle_t*()'
204
 * declarations
205
 */
206
template <>
207
class uv_handle_ptr_<uv_handle_t> : public uv_handle_ptr_base_<uv_handle_t>
208
{
209
public:
210
  CM_INHERIT_CTOR(uv_handle_ptr_, uv_handle_ptr_base_, <uv_handle_t>);
211
};
212
213
class uv_async_ptr : public uv_handle_ptr_<uv_async_t>
214
{
215
public:
216
  CM_INHERIT_CTOR(uv_async_ptr, uv_handle_ptr_, <uv_async_t>);
217
218
  int init(uv_loop_t& loop, uv_async_cb async_cb, void* data = nullptr);
219
220
  void send();
221
};
222
223
struct uv_idle_ptr : public uv_handle_ptr_<uv_idle_t>
224
{
225
  CM_INHERIT_CTOR(uv_idle_ptr, uv_handle_ptr_, <uv_idle_t>);
226
227
  int init(uv_loop_t& loop, void* data = nullptr);
228
229
  int start(uv_idle_cb cb);
230
231
  void stop();
232
};
233
234
struct uv_signal_ptr : public uv_handle_ptr_<uv_signal_t>
235
{
236
  CM_INHERIT_CTOR(uv_signal_ptr, uv_handle_ptr_, <uv_signal_t>);
237
238
  int init(uv_loop_t& loop, void* data = nullptr);
239
240
  int start(uv_signal_cb cb, int signum);
241
242
  void stop();
243
};
244
245
struct uv_pipe_ptr : public uv_handle_ptr_<uv_pipe_t>
246
{
247
  CM_INHERIT_CTOR(uv_pipe_ptr, uv_handle_ptr_, <uv_pipe_t>);
248
249
  operator uv_stream_t*() const;
250
251
  int init(uv_loop_t& loop, int ipc, void* data = nullptr);
252
};
253
254
struct uv_process_ptr : public uv_handle_ptr_<uv_process_t>
255
{
256
  CM_INHERIT_CTOR(uv_process_ptr, uv_handle_ptr_, <uv_process_t>);
257
258
  int spawn(uv_loop_t& loop, uv_process_options_t const& options,
259
            void* data = nullptr);
260
};
261
262
struct uv_timer_ptr : public uv_handle_ptr_<uv_timer_t>
263
{
264
  CM_INHERIT_CTOR(uv_timer_ptr, uv_handle_ptr_, <uv_timer_t>);
265
266
  int init(uv_loop_t& loop, void* data = nullptr);
267
268
  int start(uv_timer_cb cb, uint64_t timeout, uint64_t repeat,
269
            uv_update_time update_time);
270
271
  void stop();
272
};
273
274
struct uv_tty_ptr : public uv_handle_ptr_<uv_tty_t>
275
{
276
  CM_INHERIT_CTOR(uv_tty_ptr, uv_handle_ptr_, <uv_tty_t>);
277
278
  operator uv_stream_t*() const;
279
280
  int init(uv_loop_t& loop, int fd, int readable, void* data = nullptr);
281
};
282
283
using uv_stream_ptr = uv_handle_ptr_<uv_stream_t>;
284
using uv_handle_ptr = uv_handle_ptr_<uv_handle_t>;
285
286
#ifndef cmUVHandlePtr_cxx
287
288
extern template class uv_handle_ptr_base_<uv_handle_t>;
289
290
#  define UV_HANDLE_PTR_INSTANTIATE_EXTERN(NAME)                              \
291
    extern template class uv_handle_ptr_base_<uv_##NAME##_t>;                 \
292
    extern template class uv_handle_ptr_<uv_##NAME##_t>;
293
294
UV_HANDLE_PTR_INSTANTIATE_EXTERN(async)
295
296
UV_HANDLE_PTR_INSTANTIATE_EXTERN(idle)
297
298
UV_HANDLE_PTR_INSTANTIATE_EXTERN(signal)
299
300
UV_HANDLE_PTR_INSTANTIATE_EXTERN(pipe)
301
302
UV_HANDLE_PTR_INSTANTIATE_EXTERN(process)
303
304
UV_HANDLE_PTR_INSTANTIATE_EXTERN(stream)
305
306
UV_HANDLE_PTR_INSTANTIATE_EXTERN(timer)
307
308
UV_HANDLE_PTR_INSTANTIATE_EXTERN(tty)
309
310
#  undef UV_HANDLE_PTR_INSTANTIATE_EXTERN
311
312
#endif
313
314
/**
315
 * Wraps uv_write to add synchronous cancellation.
316
 *
317
 * libuv provides no way to synchronously cancel a write request.
318
 * Closing a write handle will cancel its pending write request, but its
319
 * callback will still be called asynchronously later with UV_ECANCELED.
320
 *
321
 * This wrapper provides a solution by handing ownership of the uv_write_t
322
 * request object to the event loop and taking it back in the callback.
323
 * Use this in combination with uv_loop_ptr to ensure the event loop
324
 * runs to completion and cleans up all resources.
325
 *
326
 * The caller may optionally provide a callback it owns with std::shared_ptr.
327
 * If the caller's lifetime ends before the write request completes, the
328
 * callback can be safely deleted and will not be called.
329
 *
330
 * The bufs array does not need to live beyond this call, but the memory
331
 * referenced by the uv_buf_t values must remain alive until the callback
332
 * is made or the stream is closed.
333
 */
334
int uv_write(uv_stream_t* handle, uv_buf_t const bufs[], unsigned int nbufs,
335
             std::weak_ptr<std::function<void(int)>> cb);
336
}