1 #ifndef SRC_REQ_WRAP_INL_H_
2 #define SRC_REQ_WRAP_INL_H_
3
4 #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
5
6 #include "req_wrap.h"
7 #include "async_wrap-inl.h"
8 #include "uv.h"
9
10 namespace node {
11
ReqWrapBase(Environment * env)12 ReqWrapBase::ReqWrapBase(Environment* env) {
13 CHECK(env->has_run_bootstrapping_code());
14 env->req_wrap_queue()->PushBack(this);
15 }
16
17 template <typename T>
ReqWrap(Environment * env,v8::Local<v8::Object> object,AsyncWrap::ProviderType provider)18 ReqWrap<T>::ReqWrap(Environment* env,
19 v8::Local<v8::Object> object,
20 AsyncWrap::ProviderType provider)
21 : AsyncWrap(env, object, provider),
22 ReqWrapBase(env) {
23 MakeWeak();
24 Reset();
25 }
26
27 template <typename T>
~ReqWrap()28 ReqWrap<T>::~ReqWrap() {}
29
30 template <typename T>
Dispatched()31 void ReqWrap<T>::Dispatched() {
32 req_.data = this;
33 }
34
35 template <typename T>
Reset()36 void ReqWrap<T>::Reset() {
37 original_callback_ = nullptr;
38 req_.data = nullptr;
39 }
40
41 template <typename T>
from_req(T * req)42 ReqWrap<T>* ReqWrap<T>::from_req(T* req) {
43 return ContainerOf(&ReqWrap<T>::req_, req);
44 }
45
46 template <typename T>
Cancel()47 void ReqWrap<T>::Cancel() {
48 if (req_.data == this) // Only cancel if already dispatched.
49 uv_cancel(reinterpret_cast<uv_req_t*>(&req_));
50 }
51
52 template <typename T>
GetAsyncWrap()53 AsyncWrap* ReqWrap<T>::GetAsyncWrap() {
54 return this;
55 }
56
57 // Below is dark template magic designed to invoke libuv functions that
58 // initialize uv_req_t instances in a unified fashion, to allow easier
59 // tracking of active/inactive requests.
60
61 // Invoke a generic libuv function that initializes uv_req_t instances.
62 // This is, unfortunately, necessary since they come in three different
63 // variants that can not all be invoked in the same way:
64 // - int uv_foo(uv_loop_t* loop, uv_req_t* request, ...);
65 // - int uv_foo(uv_req_t* request, ...);
66 // - void uv_foo(uv_req_t* request, ...);
67 template <typename ReqT, typename T>
68 struct CallLibuvFunction;
69
70 // Detect `int uv_foo(uv_loop_t* loop, uv_req_t* request, ...);`.
71 template <typename ReqT, typename... Args>
72 struct CallLibuvFunction<ReqT, int(*)(uv_loop_t*, ReqT*, Args...)> {
73 using T = int(*)(uv_loop_t*, ReqT*, Args...);
74 template <typename... PassedArgs>
75 static int Call(T fn, uv_loop_t* loop, ReqT* req, PassedArgs... args) {
76 return fn(loop, req, args...);
77 }
78 };
79
80 // Detect `int uv_foo(uv_req_t* request, ...);`.
81 template <typename ReqT, typename... Args>
82 struct CallLibuvFunction<ReqT, int(*)(ReqT*, Args...)> {
83 using T = int(*)(ReqT*, Args...);
84 template <typename... PassedArgs>
85 static int Call(T fn, uv_loop_t* loop, ReqT* req, PassedArgs... args) {
86 return fn(req, args...);
87 }
88 };
89
90 // Detect `void uv_foo(uv_req_t* request, ...);`.
91 template <typename ReqT, typename... Args>
92 struct CallLibuvFunction<ReqT, void(*)(ReqT*, Args...)> {
93 using T = void(*)(ReqT*, Args...);
94 template <typename... PassedArgs>
95 static int Call(T fn, uv_loop_t* loop, ReqT* req, PassedArgs... args) {
96 fn(req, args...);
97 return 0;
98 }
99 };
100
101 // This is slightly darker magic: This template is 'applied' to each parameter
102 // passed to the libuv function. If the parameter type (aka `T`) is a
103 // function type, it is assumed that this it is the request callback, and a
104 // wrapper that calls the original callback is created.
105 // If not, the parameter is passed through verbatim.
106 template <typename ReqT, typename T>
107 struct MakeLibuvRequestCallback {
108 static T For(ReqWrap<ReqT>* req_wrap, T v) {
109 static_assert(!is_callable<T>::value,
110 "MakeLibuvRequestCallback missed a callback");
111 return v;
112 }
113 };
114
115 // Match the `void callback(uv_req_t*, ...);` signature that all libuv
116 // callbacks use.
117 template <typename ReqT, typename... Args>
118 struct MakeLibuvRequestCallback<ReqT, void(*)(ReqT*, Args...)> {
119 using F = void(*)(ReqT* req, Args... args);
120
121 static void Wrapper(ReqT* req, Args... args) {
122 BaseObjectPtr<ReqWrap<ReqT>> req_wrap{ReqWrap<ReqT>::from_req(req)};
123 req_wrap->Detach();
124 req_wrap->env()->DecreaseWaitingRequestCounter();
125 F original_callback = reinterpret_cast<F>(req_wrap->original_callback_);
126 original_callback(req, args...);
127 }
128
129 static F For(ReqWrap<ReqT>* req_wrap, F v) {
130 CHECK_NULL(req_wrap->original_callback_);
131 req_wrap->original_callback_ =
132 reinterpret_cast<typename ReqWrap<ReqT>::callback_t>(v);
133 return Wrapper;
134 }
135 };
136
137 template <typename T>
138 template <typename LibuvFunction, typename... Args>
139 int ReqWrap<T>::Dispatch(LibuvFunction fn, Args... args) {
140 Dispatched();
141 // This expands as:
142 //
143 // int err = fn(env()->event_loop(), req(), arg1, arg2, Wrapper, arg3, ...)
144 // ^ ^ ^
145 // | | |
146 // \-- Omitted if `fn` has no | |
147 // first `uv_loop_t*` argument | |
148 // | |
149 // A function callback whose first argument | |
150 // matches the libuv request type is replaced ---/ |
151 // by the `Wrapper` method defined above |
152 // |
153 // Other (non-function) arguments are passed -----/
154 // through verbatim
155 int err = CallLibuvFunction<T, LibuvFunction>::Call(
156 fn,
157 env()->event_loop(),
158 req(),
159 MakeLibuvRequestCallback<T, Args>::For(this, args)...);
160 if (err >= 0) {
161 ClearWeak();
162 env()->IncreaseWaitingRequestCounter();
163 }
164 return err;
165 }
166
167 } // namespace node
168
169 #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
170
171 #endif // SRC_REQ_WRAP_INL_H_
172