summaryrefslogtreecommitdiff
path: root/src/req_wrap-inl.h
diff options
context:
space:
mode:
authorAnna Henningsen <anna@addaleax.net>2017-09-25 21:16:02 +0200
committerAnna Henningsen <anna@addaleax.net>2018-05-10 14:15:16 +0200
commitcac8496c2ff592198a7afd114610deccb0e178ab (patch)
treef71985006d0cb1f2348d37c60d5f617db9622439 /src/req_wrap-inl.h
parent17e289eca8f8398243df5c4006d80f7381fd08bc (diff)
downloadandroid-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.h104
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