#ifndef SRC_REQ_WRAP_INL_H_ #define SRC_REQ_WRAP_INL_H_ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #include "req_wrap.h" #include "async_wrap-inl.h" #include "uv.h" namespace node { ReqWrapBase::ReqWrapBase(Environment* env) { CHECK(env->has_run_bootstrapping_code()); env->req_wrap_queue()->PushBack(this); } template ReqWrap::ReqWrap(Environment* env, v8::Local object, AsyncWrap::ProviderType provider) : AsyncWrap(env, object, provider), ReqWrapBase(env) { Reset(); } template ReqWrap::~ReqWrap() { CHECK_EQ(false, persistent().IsEmpty()); } template void ReqWrap::Dispatched() { req_.data = this; } template void ReqWrap::Reset() { original_callback_ = nullptr; req_.data = nullptr; } template ReqWrap* ReqWrap::from_req(T* req) { return ContainerOf(&ReqWrap::req_, req); } template void ReqWrap::Cancel() { if (req_.data == this) // Only cancel if already dispatched. uv_cancel(reinterpret_cast(&req_)); } template AsyncWrap* ReqWrap::GetAsyncWrap() { return this; } // 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 struct CallLibuvFunction; // Detect `int uv_foo(uv_loop_t* loop, uv_req_t* request, ...);`. template struct CallLibuvFunction { using T = int(*)(uv_loop_t*, ReqT*, Args...); template 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 struct CallLibuvFunction { using T = int(*)(ReqT*, Args...); template 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 struct CallLibuvFunction { using T = void(*)(ReqT*, Args...); template 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 struct MakeLibuvRequestCallback { static T For(ReqWrap* req_wrap, T v) { static_assert(!is_callable::value, "MakeLibuvRequestCallback missed a callback"); return v; } }; // Match the `void callback(uv_req_t*, ...);` signature that all libuv // callbacks use. template struct MakeLibuvRequestCallback { using F = void(*)(ReqT* req, Args... args); static void Wrapper(ReqT* req, Args... args) { ReqWrap* req_wrap = ReqWrap::from_req(req); req_wrap->env()->DecreaseWaitingRequestCounter(); F original_callback = reinterpret_cast(req_wrap->original_callback_); original_callback(req, args...); } static F For(ReqWrap* req_wrap, F v) { CHECK_NULL(req_wrap->original_callback_); req_wrap->original_callback_ = reinterpret_cast::callback_t>(v); return Wrapper; } }; template template int ReqWrap::Dispatch(LibuvFunction fn, Args... args) { Dispatched(); // This expands as: // // int err = 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 int err = CallLibuvFunction::Call( fn, env()->event_loop(), req(), MakeLibuvRequestCallback::For(this, args)...); if (err >= 0) env()->IncreaseWaitingRequestCounter(); return err; } } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #endif // SRC_REQ_WRAP_INL_H_