diff options
author | Anna Henningsen <anna@addaleax.net> | 2017-09-25 21:16:02 +0200 |
---|---|---|
committer | Anna Henningsen <anna@addaleax.net> | 2018-05-10 14:15:16 +0200 |
commit | cac8496c2ff592198a7afd114610deccb0e178ab (patch) | |
tree | f71985006d0cb1f2348d37c60d5f617db9622439 /src/req_wrap-inl.h | |
parent | 17e289eca8f8398243df5c4006d80f7381fd08bc (diff) | |
download | android-node-v8-cac8496c2ff592198a7afd114610deccb0e178ab.tar.gz android-node-v8-cac8496c2ff592198a7afd114610deccb0e178ab.tar.bz2 android-node-v8-cac8496c2ff592198a7afd114610deccb0e178ab.zip |
src: unify ReqWrap libuv calling
This allows easier tracking of whether there are active `ReqWrap`s.
Many thanks for Stephen Belanger for reviewing the original version of
this commit in the Ayo.js project.
Refs: https://github.com/ayojs/ayo/pull/85
PR-URL: https://github.com/nodejs/node/pull/19377
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: James M Snell <jasnell@gmail.com>
Diffstat (limited to 'src/req_wrap-inl.h')
-rw-r--r-- | src/req_wrap-inl.h | 104 |
1 files changed, 104 insertions, 0 deletions
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 |