|
|
|
/* 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 <cassert>
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <mutex>
|
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
#include <cm/memory>
|
|
|
|
|
|
|
|
#include <cm3p/uv.h>
|
|
|
|
|
|
|
|
namespace cm {
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
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<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)
|
|
|
|
{
|
|
|
|
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<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, const uv_buf_t 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;
|
|
|
|
}
|
|
|
|
}
|