| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #define cmUVHandlePtr_cxx | |
| #include "cmUVHandlePtr.h" | |
| #include <cassert> | |
| #include <cstdlib> | |
| #include <mutex> | |
| #include <utility> | |
| #include <cm/memory> | |
| #include <cm3p/uv.h> | |
| namespace cm { | |
| 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<uv_loop_t*>(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 <typename T> | |
| static void handle_default_delete(T* type_handle) | |
| { | |
| auto* handle = reinterpret_cast<uv_handle_t*>(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 <typename T> | |
| struct uv_handle_deleter | |
| { | |
| void operator()(T* type_handle) const { handle_default_delete(type_handle); } | |
| }; | |
| template <typename T> | |
| void uv_handle_ptr_base_<T>::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<T*>(calloc(1, sizeof(T))), | |
| uv_handle_deleter<T>()); | |
| this->handle->data = data; | |
| } | |
| template <typename T> | |
| uv_handle_ptr_base_<T>::operator bool() const | |
| { | |
| return this->handle.get(); | |
| } | |
| template <typename T> | |
| void uv_handle_ptr_base_<T>::reset() | |
| { | |
| this->handle.reset(); | |
| } | |
| template <typename T> | |
| uv_handle_ptr_base_<T>::operator uv_handle_t*() const | |
| { | |
| return reinterpret_cast<uv_handle_t*>(this->handle.get()); | |
| } | |
| template <typename T> | |
| T* uv_handle_ptr_base_<T>::operator->() const noexcept | |
| { | |
| return this->handle.get(); | |
| } | |
| template <typename T> | |
| T* uv_handle_ptr_base_<T>::get() const | |
| { | |
| return this->handle.get(); | |
| } | |
| template <typename T> | |
| uv_handle_ptr_<T>::operator T*() const | |
| { | |
| return this->handle.get(); | |
| } | |
| #ifndef CMAKE_BOOTSTRAP | |
| template <> | |
| struct uv_handle_deleter<uv_async_t> | |
| { | |
| /*** | |
| * 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<std::mutex> handleMutex; | |
| uv_handle_deleter() | |
| : handleMutex(std::make_shared<std::mutex>()) | |
| { | |
| } | |
| void operator()(uv_async_t* handle) | |
| { | |
| std::lock_guard<std::mutex> lock(*this->handleMutex); | |
| handle_default_delete(handle); | |
| } | |
| }; | |
| void uv_async_ptr::send() | |
| { | |
| auto* deleter = | |
| std::get_deleter<uv_handle_deleter<uv_async_t>>(this->handle); | |
| assert(deleter); | |
| std::lock_guard<std::mutex> 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<uv_signal_t> | |
| { | |
| 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<uv_stream_t*>(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, | |
| uv_update_time update_time) | |
| { | |
| assert(this->handle); | |
| if (update_time == uv_update_time::yes) { | |
| ::uv_update_time(this->handle->loop); | |
| } | |
| 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<uv_stream_t*>(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_<uv_handle_t>; | |
| #define UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(NAME) \ | |
| template class uv_handle_ptr_base_<uv_##NAME##_t>; \ | |
| template class uv_handle_ptr_<uv_##NAME##_t>; | |
| 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<std::function<void(int)>> cb_; | |
| write_req(std::weak_ptr<std::function<void(int)>> 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<write_req> self(static_cast<write_req*>(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, uv_buf_t const bufs[], unsigned int nbufs, | |
| std::weak_ptr<std::function<void(int)>> cb) | |
| { | |
| auto req = cm::make_unique<write_req>(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<void>(req.release()); | |
| } | |
| return status; | |
| } | |
| } |