@@ -43,6 +43,110 @@ void ReqWrap<T>::Cancel() {
4343 uv_cancel (reinterpret_cast <uv_req_t *>(&req_));
4444}
4545
46+ // Below is dark template magic designed to invoke libuv functions that
47+ // initialize uv_req_t instances in a unified fashion, to allow easier
48+ // tracking of active/inactive requests.
49+
50+ // Invoke a generic libuv function that initializes uv_req_t instances.
51+ // This is, unfortunately, necessary since they come in three different
52+ // variants that can not all be invoked in the same way:
53+ // - int uv_foo(uv_loop_t* loop, uv_req_t* request, ...);
54+ // - int uv_foo(uv_req_t* request, ...);
55+ // - void uv_foo(uv_req_t* request, ...);
56+ template <typename ReqT, typename T>
57+ struct CallLibuvFunction ;
58+
59+ // Detect `int uv_foo(uv_loop_t* loop, uv_req_t* request, ...);`.
60+ template <typename ReqT, typename ... Args>
61+ struct CallLibuvFunction <ReqT, int (*)(uv_loop_t *, ReqT*, Args...)> {
62+ using T = int (*)(uv_loop_t *, ReqT*, Args...);
63+ template <typename ... PassedArgs>
64+ static int Call (T fn, uv_loop_t * loop, ReqT* req, PassedArgs... args) {
65+ return fn (loop, req, args...);
66+ }
67+ };
68+
69+ // Detect `int uv_foo(uv_req_t* request, ...);`.
70+ template <typename ReqT, typename ... Args>
71+ struct CallLibuvFunction <ReqT, int (*)(ReqT*, Args...)> {
72+ using T = int (*)(ReqT*, Args...);
73+ template <typename ... PassedArgs>
74+ static int Call (T fn, uv_loop_t * loop, ReqT* req, PassedArgs... args) {
75+ return fn (req, args...);
76+ }
77+ };
78+
79+ // Detect `void uv_foo(uv_req_t* request, ...);`.
80+ template <typename ReqT, typename ... Args>
81+ struct CallLibuvFunction <ReqT, void (*)(ReqT*, Args...)> {
82+ using T = void (*)(ReqT*, Args...);
83+ template <typename ... PassedArgs>
84+ static int Call (T fn, uv_loop_t * loop, ReqT* req, PassedArgs... args) {
85+ fn (req, args...);
86+ return 0 ;
87+ }
88+ };
89+
90+ // This is slightly darker magic: This template is 'applied' to each parameter
91+ // passed to the libuv function. If the parameter type (aka `T`) is a
92+ // function type, it is assumed that this it is the request callback, and a
93+ // wrapper that calls the original callback is created.
94+ // If not, the parameter is passed through verbatim.
95+ template <typename ReqT, typename T>
96+ struct MakeLibuvRequestCallback {
97+ static T For (ReqWrap<ReqT>* req_wrap, T v) {
98+ static_assert (!std::is_function<T>::value,
99+ " MakeLibuvRequestCallback missed a callback" );
100+ return v;
101+ }
102+ };
103+
104+ // Match the `void callback(uv_req_t*, ...);` signature that all libuv
105+ // callbacks use.
106+ template <typename ReqT, typename ... Args>
107+ struct MakeLibuvRequestCallback <ReqT, void (*)(ReqT*, Args...)> {
108+ using F = void (*)(ReqT* req, Args... args);
109+
110+ static void Wrapper (ReqT* req, Args... args) {
111+ ReqWrap<ReqT>* req_wrap = ContainerOf (&ReqWrap<ReqT>::req_, req);
112+ F original_callback = reinterpret_cast <F>(req_wrap->original_callback_ );
113+ original_callback (req, args...);
114+ }
115+
116+ static F For (ReqWrap<ReqT>* req_wrap, F v) {
117+ CHECK_EQ (req_wrap->original_callback_ , nullptr );
118+ req_wrap->original_callback_ =
119+ reinterpret_cast <typename ReqWrap<ReqT>::callback_t >(v);
120+ return Wrapper;
121+ }
122+ };
123+
124+ template <typename T>
125+ template <typename LibuvFunction, typename ... Args>
126+ int ReqWrap<T>::Dispatch(LibuvFunction fn, Args... args) {
127+ Dispatched ();
128+
129+ // This expands as:
130+ //
131+ // return fn(env()->event_loop(), req(), arg1, arg2, Wrapper, arg3, ...)
132+ // ^ ^ ^
133+ // | | |
134+ // \-- Omitted if `fn` has no | |
135+ // first `uv_loop_t*` argument | |
136+ // | |
137+ // A function callback whose first argument | |
138+ // matches the libuv request type is replaced ---/ |
139+ // by the `Wrapper` method defined above |
140+ // |
141+ // Other (non-function) arguments are passed -----/
142+ // through verbatim
143+ return CallLibuvFunction<T, LibuvFunction>::Call (
144+ fn,
145+ env ()->event_loop (),
146+ req (),
147+ MakeLibuvRequestCallback<T, Args>::For (this , args)...);
148+ }
149+
46150} // namespace node
47151
48152#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
0 commit comments