/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ #define cmUVHandlePtr_cxx #include "cmUVHandlePtr.h" #include #include #include #include #include #include namespace cm { template struct uv_handle_deleter; struct uv_loop_deleter { void operator()(uv_loop_t* loop) const; }; void uv_loop_deleter::operator()(uv_loop_t* loop) const { uv_run(loop, UV_RUN_DEFAULT); int result = uv_loop_close(loop); (void)result; assert(result >= 0); free(loop); } int uv_loop_ptr::init(void* data) { this->reset(); this->loop.reset(static_cast(calloc(1, sizeof(uv_loop_t))), uv_loop_deleter()); this->loop->data = data; return uv_loop_init(this->loop.get()); } void uv_loop_ptr::reset() { this->loop.reset(); } uv_loop_ptr::operator uv_loop_t*() const { return this->loop.get(); } uv_loop_t* uv_loop_ptr::operator->() const noexcept { return this->loop.get(); } uv_loop_t& uv_loop_ptr::operator*() const { return *this->loop; } uv_loop_t* uv_loop_ptr::get() const { return this->loop.get(); } template static void handle_default_delete(T* type_handle) { auto* handle = reinterpret_cast(type_handle); if (handle) { assert(!uv_is_closing(handle)); if (!uv_is_closing(handle)) { uv_close(handle, [](uv_handle_t* h) { free(h); }); } } } /** * Encapsulates delete logic for a given handle type T */ template struct uv_handle_deleter { void operator()(T* type_handle) const { handle_default_delete(type_handle); } }; template void uv_handle_ptr_base_::allocate(void* data) { this->reset(); /* We use calloc since we know all these types are c structs and we just want to 0 init them. New would do the same thing; but casting from uv_handle_t to certain other types -- namely uv_timer_t -- triggers a cast_align warning on certain systems. */ this->handle.reset(static_cast(calloc(1, sizeof(T))), uv_handle_deleter()); this->handle->data = data; } template uv_handle_ptr_base_::operator bool() const { return this->handle.get(); } template void uv_handle_ptr_base_::reset() { this->handle.reset(); } template uv_handle_ptr_base_::operator uv_handle_t*() const { return reinterpret_cast(this->handle.get()); } template T* uv_handle_ptr_base_::operator->() const noexcept { return this->handle.get(); } template T* uv_handle_ptr_base_::get() const { return this->handle.get(); } template uv_handle_ptr_::operator T*() const { return this->handle.get(); } #ifndef CMAKE_BOOTSTRAP template <> struct uv_handle_deleter { /*** * While uv_async_send is itself thread-safe, there are * no strong guarantees that close hasn't already been * called on the handle; and that it might be deleted * as the send call goes through. This mutex guards * against that. * * The shared_ptr here is to allow for copy construction * which is mandated by the standard for Deleter on * shared_ptrs. */ std::shared_ptr handleMutex; uv_handle_deleter() : handleMutex(std::make_shared()) { } void operator()(uv_async_t* handle) { std::lock_guard lock(*this->handleMutex); handle_default_delete(handle); } }; void uv_async_ptr::send() { auto* deleter = std::get_deleter>(this->handle); assert(deleter); std::lock_guard lock(*deleter->handleMutex); if (this->handle) { uv_async_send(*this); } } int uv_async_ptr::init(uv_loop_t& loop, uv_async_cb async_cb, void* data) { this->allocate(data); return uv_async_init(&loop, this->handle.get(), async_cb); } #endif template <> struct uv_handle_deleter { void operator()(uv_signal_t* handle) const { if (handle) { uv_signal_stop(handle); handle_default_delete(handle); } } }; int uv_signal_ptr::init(uv_loop_t& loop, void* data) { this->allocate(data); return uv_signal_init(&loop, this->handle.get()); } int uv_signal_ptr::start(uv_signal_cb cb, int signum) { assert(this->handle); return uv_signal_start(*this, cb, signum); } void uv_signal_ptr::stop() { if (this->handle) { uv_signal_stop(*this); } } int uv_pipe_ptr::init(uv_loop_t& loop, int ipc, void* data) { this->allocate(data); return uv_pipe_init(&loop, *this, ipc); } uv_pipe_ptr::operator uv_stream_t*() const { return reinterpret_cast(this->handle.get()); } int uv_process_ptr::spawn(uv_loop_t& loop, uv_process_options_t const& options, void* data) { this->allocate(data); return uv_spawn(&loop, *this, &options); } int uv_timer_ptr::init(uv_loop_t& loop, void* data) { this->allocate(data); return uv_timer_init(&loop, *this); } int uv_timer_ptr::start(uv_timer_cb cb, uint64_t timeout, uint64_t repeat) { assert(this->handle); return uv_timer_start(*this, cb, timeout, repeat); } void uv_timer_ptr::stop() { assert(this->handle); uv_timer_stop(*this); } #ifndef CMAKE_BOOTSTRAP uv_tty_ptr::operator uv_stream_t*() const { return reinterpret_cast(this->handle.get()); } int uv_tty_ptr::init(uv_loop_t& loop, int fd, int readable, void* data) { this->allocate(data); return uv_tty_init(&loop, *this, fd, readable); } #endif int uv_idle_ptr::init(uv_loop_t& loop, void* data) { this->allocate(data); return uv_idle_init(&loop, *this); } int uv_idle_ptr::start(uv_idle_cb cb) { assert(this->handle); return uv_idle_start(*this, cb); } void uv_idle_ptr::stop() { assert(this->handle); uv_idle_stop(*this); } template class uv_handle_ptr_base_; #define UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(NAME) \ template class uv_handle_ptr_base_; \ template class uv_handle_ptr_; UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(idle) UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(signal) UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(pipe) UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(stream) UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(process) UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(timer) #ifndef CMAKE_BOOTSTRAP UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(async) UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(tty) #endif namespace { struct write_req : public uv_write_t { std::weak_ptr> cb_; write_req(std::weak_ptr> wcb) : cb_(std::move(wcb)) { } }; void write_req_cb(uv_write_t* req, int status) { // Ownership has been transferred from the event loop. std::unique_ptr self(static_cast(req)); // Notify the original uv_write caller if it is still interested. if (auto cb = self->cb_.lock()) { (*cb)(status); } } } int uv_write(uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, std::weak_ptr> cb) { auto req = cm::make_unique(std::move(cb)); int status = uv_write(req.get(), handle, bufs, nbufs, write_req_cb); if (status == 0) { // Ownership has been transferred to the event loop. static_cast(req.release()); } return status; } }