| #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 <typename T> |
| ReqWrap<T>::ReqWrap(Environment* env, |
| v8::Local<v8::Object> object, |
| AsyncWrap::ProviderType provider) |
| : AsyncWrap(env, object, provider), |
| ReqWrapBase(env) { |
| Reset(); |
| } |
| |
| template <typename T> |
| ReqWrap<T>::~ReqWrap() { |
| CHECK_EQ(false, persistent().IsEmpty()); |
| } |
| |
| template <typename T> |
| void ReqWrap<T>::Dispatched() { |
| req_.data = this; |
| } |
| |
| template <typename T> |
| void ReqWrap<T>::Reset() { |
| original_callback_ = nullptr; |
| req_.data = nullptr; |
| } |
| |
| template <typename T> |
| ReqWrap<T>* ReqWrap<T>::from_req(T* req) { |
| return ContainerOf(&ReqWrap<T>::req_, req); |
| } |
| |
| template <typename T> |
| void ReqWrap<T>::Cancel() { |
| if (req_.data == this) // Only cancel if already dispatched. |
| uv_cancel(reinterpret_cast<uv_req_t*>(&req_)); |
| } |
| |
| template <typename T> |
| AsyncWrap* ReqWrap<T>::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 <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(!is_callable<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 = ReqWrap<ReqT>::from_req(req); |
| req_wrap->env()->DecreaseWaitingRequestCounter(); |
| F original_callback = reinterpret_cast<F>(req_wrap->original_callback_); |
| original_callback(req, args...); |
| } |
| |
| static F For(ReqWrap<ReqT>* req_wrap, F v) { |
| CHECK_NULL(req_wrap->original_callback_); |
| 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: |
| // |
| // 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<T, LibuvFunction>::Call( |
| fn, |
| env()->event_loop(), |
| req(), |
| MakeLibuvRequestCallback<T, Args>::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_ |