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