diff options
-rw-r--r-- | src/cares_wrap.cc | 24 | ||||
-rw-r--r-- | src/node_file.cc | 40 | ||||
-rw-r--r-- | src/pipe_wrap.cc | 9 | ||||
-rw-r--r-- | src/req_wrap-inl.h | 104 | ||||
-rw-r--r-- | src/req_wrap.h | 13 | ||||
-rw-r--r-- | src/tcp_wrap.cc | 18 | ||||
-rw-r--r-- | src/udp_wrap.cc | 13 |
7 files changed, 165 insertions, 56 deletions
diff --git a/src/cares_wrap.cc b/src/cares_wrap.cc index ae253d40ca..f829eb2b01 100644 --- a/src/cares_wrap.cc +++ b/src/cares_wrap.cc @@ -515,7 +515,7 @@ ChannelWrap::~ChannelWrap() { void ChannelWrap::CleanupTimer() { if (timer_handle_ == nullptr) return; - env()->CloseHandle(timer_handle_, [](uv_timer_t* handle){ delete handle; }); + env()->CloseHandle(timer_handle_, [](uv_timer_t* handle) { delete handle; }); timer_handle_ = nullptr; } @@ -1927,13 +1927,11 @@ void GetAddrInfo(const FunctionCallbackInfo<Value>& args) { hints.ai_socktype = SOCK_STREAM; hints.ai_flags = flags; - int err = uv_getaddrinfo(env->event_loop(), - req_wrap->req(), - AfterGetAddrInfo, - *hostname, - nullptr, - &hints); - req_wrap->Dispatched(); + int err = req_wrap->Dispatch(uv_getaddrinfo, + AfterGetAddrInfo, + *hostname, + nullptr, + &hints); if (err) delete req_wrap; @@ -1957,12 +1955,10 @@ void GetNameInfo(const FunctionCallbackInfo<Value>& args) { GetNameInfoReqWrap* req_wrap = new GetNameInfoReqWrap(env, req_wrap_obj); - int err = uv_getnameinfo(env->event_loop(), - req_wrap->req(), - AfterGetNameInfo, - (struct sockaddr*)&addr, - NI_NAMEREQD); - req_wrap->Dispatched(); + int err = req_wrap->Dispatch(uv_getnameinfo, + AfterGetNameInfo, + reinterpret_cast<struct sockaddr*>(&addr), + NI_NAMEREQD); if (err) delete req_wrap; diff --git a/src/node_file.cc b/src/node_file.cc index 97b957eed6..713dcbf633 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -89,6 +89,11 @@ using v8::Value; TRACE_EVENT_END(TRACING_CATEGORY_NODE2(fs, sync), TRACE_NAME(syscall), \ ##__VA_ARGS__); +// We sometimes need to convert a C++ lambda function to a raw C-style function. +// This is helpful, because ReqWrap::Dispatch() does not recognize lambda +// functions, and thus does not wrap them properly. +typedef void(*uv_fs_callback_t)(uv_fs_t*); + // The FileHandle object wraps a file descriptor and will close it on garbage // collection if necessary. If that happens, a process warning will be // emitted (or a fatal exception will occur if the fd cannot be closed.) @@ -216,7 +221,7 @@ inline MaybeLocal<Promise> FileHandle::ClosePromise() { if (!closed_ && !closing_) { closing_ = true; CloseReq* req = new CloseReq(env(), promise, object()); - auto AfterClose = [](uv_fs_t* req) { + auto AfterClose = uv_fs_callback_t{[](uv_fs_t* req) { CloseReq* close = static_cast<CloseReq*>(req->data); CHECK_NE(close, nullptr); close->file_handle()->AfterClose(); @@ -227,9 +232,8 @@ inline MaybeLocal<Promise> FileHandle::ClosePromise() { close->Resolve(); } delete close; - }; - req->Dispatched(); - int ret = uv_fs_close(env()->event_loop(), req->req(), fd_, AfterClose); + }}; + int ret = req->Dispatch(uv_fs_close, fd_, AfterClose); if (ret < 0) { req->Reject(UVException(isolate, ret, "close")); delete req; @@ -309,17 +313,15 @@ int FileHandle::ReadStart() { recommended_read = read_length_; read_wrap->buffer_ = EmitAlloc(recommended_read); - read_wrap->Dispatched(); current_read_ = std::move(read_wrap); - uv_fs_read(env()->event_loop(), - current_read_->req(), - fd_, - ¤t_read_->buffer_, - 1, - read_offset_, - [](uv_fs_t* req) { + current_read_->Dispatch(uv_fs_read, + fd_, + ¤t_read_->buffer_, + 1, + read_offset_, + uv_fs_callback_t{[](uv_fs_t* req) { FileHandle* handle; { FileHandleReadWrap* req_wrap = FileHandleReadWrap::from_req(req); @@ -342,8 +344,10 @@ int FileHandle::ReadStart() { // once we’re exiting the current scope. constexpr size_t wanted_freelist_fill = 100; auto& freelist = handle->env()->file_handle_read_wrap_freelist(); - if (freelist.size() < wanted_freelist_fill) + if (freelist.size() < wanted_freelist_fill) { + read_wrap->Reset(); freelist.emplace_back(std::move(read_wrap)); + } if (result >= 0) { // Read at most as many bytes as we originally planned to. @@ -370,7 +374,7 @@ int FileHandle::ReadStart() { // Start over, if EmitRead() didn’t tell us to stop. if (handle->reading_) handle->ReadStart(); - }); + }}); return 0; } @@ -389,8 +393,7 @@ ShutdownWrap* FileHandle::CreateShutdownWrap(Local<Object> object) { int FileHandle::DoShutdown(ShutdownWrap* req_wrap) { FileHandleCloseWrap* wrap = static_cast<FileHandleCloseWrap*>(req_wrap); closing_ = true; - wrap->Dispatched(); - uv_fs_close(env()->event_loop(), wrap->req(), fd_, [](uv_fs_t* req) { + wrap->Dispatch(uv_fs_close, fd_, uv_fs_callback_t{[](uv_fs_t* req) { FileHandleCloseWrap* wrap = static_cast<FileHandleCloseWrap*>( FileHandleCloseWrap::from_req(req)); FileHandle* handle = static_cast<FileHandle*>(wrap->stream()); @@ -399,7 +402,7 @@ int FileHandle::DoShutdown(ShutdownWrap* req_wrap) { int result = req->result; uv_fs_req_cleanup(req); wrap->Done(result); - }); + }}); return 0; } @@ -616,8 +619,7 @@ inline FSReqBase* AsyncDestCall(Environment* env, enum encoding enc, uv_fs_cb after, Func fn, Args... fn_args) { CHECK_NE(req_wrap, nullptr); req_wrap->Init(syscall, dest, len, enc); - int err = fn(env->event_loop(), req_wrap->req(), fn_args..., after); - req_wrap->Dispatched(); + int err = req_wrap->Dispatch(fn, fn_args..., after); if (err < 0) { uv_fs_t* uv_req = req_wrap->req(); uv_req->result = err; diff --git a/src/pipe_wrap.cc b/src/pipe_wrap.cc index da7cb9e3ab..7ec5bdf15b 100644 --- a/src/pipe_wrap.cc +++ b/src/pipe_wrap.cc @@ -224,11 +224,10 @@ void PipeWrap::Connect(const FunctionCallbackInfo<Value>& args) { ConnectWrap* req_wrap = new ConnectWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_PIPECONNECTWRAP); - uv_pipe_connect(req_wrap->req(), - &wrap->handle_, - *name, - AfterConnect); - req_wrap->Dispatched(); + req_wrap->Dispatch(uv_pipe_connect, + &wrap->handle_, + *name, + AfterConnect); args.GetReturnValue().Set(0); // uv_pipe_connect() doesn't return errors. } diff --git a/src/req_wrap-inl.h b/src/req_wrap-inl.h index ab5051e41d..54abf74430 100644 --- a/src/req_wrap-inl.h +++ b/src/req_wrap-inl.h @@ -43,6 +43,110 @@ void ReqWrap<T>::Cancel() { uv_cancel(reinterpret_cast<uv_req_t*>(&req_)); } +// Below is dark template magic designed to invoke libuv functions that +// initialize uv_req_t instances in a unified fashion, to allow easier +// tracking of active/inactive requests. + +// Invoke a generic libuv function that initializes uv_req_t instances. +// This is, unfortunately, necessary since they come in three different +// variants that can not all be invoked in the same way: +// - int uv_foo(uv_loop_t* loop, uv_req_t* request, ...); +// - int uv_foo(uv_req_t* request, ...); +// - void uv_foo(uv_req_t* request, ...); +template <typename ReqT, typename T> +struct CallLibuvFunction; + +// Detect `int uv_foo(uv_loop_t* loop, uv_req_t* request, ...);`. +template <typename ReqT, typename... Args> +struct CallLibuvFunction<ReqT, int(*)(uv_loop_t*, ReqT*, Args...)> { + using T = int(*)(uv_loop_t*, ReqT*, Args...); + template <typename... PassedArgs> + static int Call(T fn, uv_loop_t* loop, ReqT* req, PassedArgs... args) { + return fn(loop, req, args...); + } +}; + +// Detect `int uv_foo(uv_req_t* request, ...);`. +template <typename ReqT, typename... Args> +struct CallLibuvFunction<ReqT, int(*)(ReqT*, Args...)> { + using T = int(*)(ReqT*, Args...); + template <typename... PassedArgs> + static int Call(T fn, uv_loop_t* loop, ReqT* req, PassedArgs... args) { + return fn(req, args...); + } +}; + +// Detect `void uv_foo(uv_req_t* request, ...);`. +template <typename ReqT, typename... Args> +struct CallLibuvFunction<ReqT, void(*)(ReqT*, Args...)> { + using T = void(*)(ReqT*, Args...); + template <typename... PassedArgs> + static int Call(T fn, uv_loop_t* loop, ReqT* req, PassedArgs... args) { + fn(req, args...); + return 0; + } +}; + +// This is slightly darker magic: This template is 'applied' to each parameter +// passed to the libuv function. If the parameter type (aka `T`) is a +// function type, it is assumed that this it is the request callback, and a +// wrapper that calls the original callback is created. +// If not, the parameter is passed through verbatim. +template <typename ReqT, typename T> +struct MakeLibuvRequestCallback { + static T For(ReqWrap<ReqT>* req_wrap, T v) { + static_assert(!std::is_function<T>::value, + "MakeLibuvRequestCallback missed a callback"); + return v; + } +}; + +// Match the `void callback(uv_req_t*, ...);` signature that all libuv +// callbacks use. +template <typename ReqT, typename... Args> +struct MakeLibuvRequestCallback<ReqT, void(*)(ReqT*, Args...)> { + using F = void(*)(ReqT* req, Args... args); + + static void Wrapper(ReqT* req, Args... args) { + ReqWrap<ReqT>* req_wrap = ContainerOf(&ReqWrap<ReqT>::req_, req); + F original_callback = reinterpret_cast<F>(req_wrap->original_callback_); + original_callback(req, args...); + } + + static F For(ReqWrap<ReqT>* req_wrap, F v) { + CHECK_EQ(req_wrap->original_callback_, nullptr); + req_wrap->original_callback_ = + reinterpret_cast<typename ReqWrap<ReqT>::callback_t>(v); + return Wrapper; + } +}; + +template <typename T> +template <typename LibuvFunction, typename... Args> +int ReqWrap<T>::Dispatch(LibuvFunction fn, Args... args) { + Dispatched(); + + // This expands as: + // + // return fn(env()->event_loop(), req(), arg1, arg2, Wrapper, arg3, ...) + // ^ ^ ^ + // | | | + // \-- Omitted if `fn` has no | | + // first `uv_loop_t*` argument | | + // | | + // A function callback whose first argument | | + // matches the libuv request type is replaced ---/ | + // by the `Wrapper` method defined above | + // | + // Other (non-function) arguments are passed -----/ + // through verbatim + return CallLibuvFunction<T, LibuvFunction>::Call( + fn, + env()->event_loop(), + req(), + MakeLibuvRequestCallback<T, Args>::For(this, args)...); +} + } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/src/req_wrap.h b/src/req_wrap.h index 4d6a89d743..d181817218 100644 --- a/src/req_wrap.h +++ b/src/req_wrap.h @@ -17,17 +17,28 @@ class ReqWrap : public AsyncWrap { v8::Local<v8::Object> object, AsyncWrap::ProviderType provider); inline ~ReqWrap() override; - inline void Dispatched(); // Call this after the req has been dispatched. + // Call this after the req has been dispatched, if that did not already + // happen by using Dispatch(). + inline void Dispatched(); T* req() { return &req_; } inline void Cancel(); static ReqWrap* from_req(T* req); + template <typename LibuvFunction, typename... Args> + inline int Dispatch(LibuvFunction fn, Args... args); + private: friend class Environment; friend int GenDebugSymbols(); + template <typename ReqT, typename U> + friend struct MakeLibuvRequestCallback; + ListNode<ReqWrap> req_wrap_queue_; + typedef void (*callback_t)(); + callback_t original_callback_ = nullptr; + protected: // req_wrap_queue_ needs to be at a fixed offset from the start of the class // because it is used by ContainerOf to calculate the address of the embedding diff --git a/src/tcp_wrap.cc b/src/tcp_wrap.cc index 3ccd157159..70c60fa47c 100644 --- a/src/tcp_wrap.cc +++ b/src/tcp_wrap.cc @@ -287,11 +287,10 @@ void TCPWrap::Connect(const FunctionCallbackInfo<Value>& args) { AsyncHooks::DefaultTriggerAsyncIdScope trigger_scope(wrap); ConnectWrap* req_wrap = new ConnectWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_TCPCONNECTWRAP); - err = uv_tcp_connect(req_wrap->req(), - &wrap->handle_, - reinterpret_cast<const sockaddr*>(&addr), - AfterConnect); - req_wrap->Dispatched(); + err = req_wrap->Dispatch(uv_tcp_connect, + &wrap->handle_, + reinterpret_cast<const sockaddr*>(&addr), + AfterConnect); if (err) delete req_wrap; } @@ -323,11 +322,10 @@ void TCPWrap::Connect6(const FunctionCallbackInfo<Value>& args) { AsyncHooks::DefaultTriggerAsyncIdScope trigger_scope(wrap); ConnectWrap* req_wrap = new ConnectWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_TCPCONNECTWRAP); - err = uv_tcp_connect(req_wrap->req(), - &wrap->handle_, - reinterpret_cast<const sockaddr*>(&addr), - AfterConnect); - req_wrap->Dispatched(); + err = req_wrap->Dispatch(uv_tcp_connect, + &wrap->handle_, + reinterpret_cast<const sockaddr*>(&addr), + AfterConnect); if (err) delete req_wrap; } diff --git a/src/udp_wrap.cc b/src/udp_wrap.cc index 414fe07eab..1d1ded449b 100644 --- a/src/udp_wrap.cc +++ b/src/udp_wrap.cc @@ -380,15 +380,14 @@ void UDPWrap::DoSend(const FunctionCallbackInfo<Value>& args, int family) { } if (err == 0) { - err = uv_udp_send(req_wrap->req(), - &wrap->handle_, - *bufs, - count, - reinterpret_cast<const sockaddr*>(&addr), - OnSend); + err = req_wrap->Dispatch(uv_udp_send, + &wrap->handle_, + *bufs, + count, + reinterpret_cast<const sockaddr*>(&addr), + OnSend); } - req_wrap->Dispatched(); if (err) delete req_wrap; |