/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 | | } |