Skip to content

Commit 75aad90

Browse files
committed
src: unify ReqWrap libuv calling
This allows easier tracking of whether there are active `ReqWrap`s. Many thanks for Stephen Belanger for reviewing the original version of this commit in the Ayo.js project. Refs: ayojs/ayo#85 PR-URL: #19377 Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent e253edb commit 75aad90

File tree

7 files changed

+165
-56
lines changed

7 files changed

+165
-56
lines changed

src/cares_wrap.cc

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ ChannelWrap::~ChannelWrap() {
515515
void ChannelWrap::CleanupTimer() {
516516
if (timer_handle_ == nullptr) return;
517517

518-
env()->CloseHandle(timer_handle_, [](uv_timer_t* handle){ delete handle; });
518+
env()->CloseHandle(timer_handle_, [](uv_timer_t* handle) { delete handle; });
519519
timer_handle_ = nullptr;
520520
}
521521

@@ -1927,13 +1927,11 @@ void GetAddrInfo(const FunctionCallbackInfo<Value>& args) {
19271927
hints.ai_socktype = SOCK_STREAM;
19281928
hints.ai_flags = flags;
19291929

1930-
int err = uv_getaddrinfo(env->event_loop(),
1931-
req_wrap->req(),
1932-
AfterGetAddrInfo,
1933-
*hostname,
1934-
nullptr,
1935-
&hints);
1936-
req_wrap->Dispatched();
1930+
int err = req_wrap->Dispatch(uv_getaddrinfo,
1931+
AfterGetAddrInfo,
1932+
*hostname,
1933+
nullptr,
1934+
&hints);
19371935
if (err)
19381936
delete req_wrap;
19391937

@@ -1957,12 +1955,10 @@ void GetNameInfo(const FunctionCallbackInfo<Value>& args) {
19571955

19581956
GetNameInfoReqWrap* req_wrap = new GetNameInfoReqWrap(env, req_wrap_obj);
19591957

1960-
int err = uv_getnameinfo(env->event_loop(),
1961-
req_wrap->req(),
1962-
AfterGetNameInfo,
1963-
(struct sockaddr*)&addr,
1964-
NI_NAMEREQD);
1965-
req_wrap->Dispatched();
1958+
int err = req_wrap->Dispatch(uv_getnameinfo,
1959+
AfterGetNameInfo,
1960+
reinterpret_cast<struct sockaddr*>(&addr),
1961+
NI_NAMEREQD);
19661962
if (err)
19671963
delete req_wrap;
19681964

src/node_file.cc

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ using v8::Value;
8989
TRACE_EVENT_END(TRACING_CATEGORY_NODE2(fs, sync), TRACE_NAME(syscall), \
9090
##__VA_ARGS__);
9191

92+
// We sometimes need to convert a C++ lambda function to a raw C-style function.
93+
// This is helpful, because ReqWrap::Dispatch() does not recognize lambda
94+
// functions, and thus does not wrap them properly.
95+
typedef void(*uv_fs_callback_t)(uv_fs_t*);
96+
9297
// The FileHandle object wraps a file descriptor and will close it on garbage
9398
// collection if necessary. If that happens, a process warning will be
9499
// emitted (or a fatal exception will occur if the fd cannot be closed.)
@@ -216,7 +221,7 @@ inline MaybeLocal<Promise> FileHandle::ClosePromise() {
216221
if (!closed_ && !closing_) {
217222
closing_ = true;
218223
CloseReq* req = new CloseReq(env(), promise, object());
219-
auto AfterClose = [](uv_fs_t* req) {
224+
auto AfterClose = uv_fs_callback_t{[](uv_fs_t* req) {
220225
CloseReq* close = static_cast<CloseReq*>(req->data);
221226
CHECK_NE(close, nullptr);
222227
close->file_handle()->AfterClose();
@@ -227,9 +232,8 @@ inline MaybeLocal<Promise> FileHandle::ClosePromise() {
227232
close->Resolve();
228233
}
229234
delete close;
230-
};
231-
req->Dispatched();
232-
int ret = uv_fs_close(env()->event_loop(), req->req(), fd_, AfterClose);
235+
}};
236+
int ret = req->Dispatch(uv_fs_close, fd_, AfterClose);
233237
if (ret < 0) {
234238
req->Reject(UVException(isolate, ret, "close"));
235239
delete req;
@@ -309,17 +313,15 @@ int FileHandle::ReadStart() {
309313
recommended_read = read_length_;
310314

311315
read_wrap->buffer_ = EmitAlloc(recommended_read);
312-
read_wrap->Dispatched();
313316

314317
current_read_ = std::move(read_wrap);
315318

316-
uv_fs_read(env()->event_loop(),
317-
current_read_->req(),
318-
fd_,
319-
&current_read_->buffer_,
320-
1,
321-
read_offset_,
322-
[](uv_fs_t* req) {
319+
current_read_->Dispatch(uv_fs_read,
320+
fd_,
321+
&current_read_->buffer_,
322+
1,
323+
read_offset_,
324+
uv_fs_callback_t{[](uv_fs_t* req) {
323325
FileHandle* handle;
324326
{
325327
FileHandleReadWrap* req_wrap = FileHandleReadWrap::from_req(req);
@@ -342,8 +344,10 @@ int FileHandle::ReadStart() {
342344
// once we’re exiting the current scope.
343345
constexpr size_t wanted_freelist_fill = 100;
344346
auto& freelist = handle->env()->file_handle_read_wrap_freelist();
345-
if (freelist.size() < wanted_freelist_fill)
347+
if (freelist.size() < wanted_freelist_fill) {
348+
read_wrap->Reset();
346349
freelist.emplace_back(std::move(read_wrap));
350+
}
347351

348352
if (result >= 0) {
349353
// Read at most as many bytes as we originally planned to.
@@ -370,7 +374,7 @@ int FileHandle::ReadStart() {
370374
// Start over, if EmitRead() didn’t tell us to stop.
371375
if (handle->reading_)
372376
handle->ReadStart();
373-
});
377+
}});
374378

375379
return 0;
376380
}
@@ -389,8 +393,7 @@ ShutdownWrap* FileHandle::CreateShutdownWrap(Local<Object> object) {
389393
int FileHandle::DoShutdown(ShutdownWrap* req_wrap) {
390394
FileHandleCloseWrap* wrap = static_cast<FileHandleCloseWrap*>(req_wrap);
391395
closing_ = true;
392-
wrap->Dispatched();
393-
uv_fs_close(env()->event_loop(), wrap->req(), fd_, [](uv_fs_t* req) {
396+
wrap->Dispatch(uv_fs_close, fd_, uv_fs_callback_t{[](uv_fs_t* req) {
394397
FileHandleCloseWrap* wrap = static_cast<FileHandleCloseWrap*>(
395398
FileHandleCloseWrap::from_req(req));
396399
FileHandle* handle = static_cast<FileHandle*>(wrap->stream());
@@ -399,7 +402,7 @@ int FileHandle::DoShutdown(ShutdownWrap* req_wrap) {
399402
int result = req->result;
400403
uv_fs_req_cleanup(req);
401404
wrap->Done(result);
402-
});
405+
}});
403406

404407
return 0;
405408
}
@@ -616,8 +619,7 @@ inline FSReqBase* AsyncDestCall(Environment* env,
616619
enum encoding enc, uv_fs_cb after, Func fn, Args... fn_args) {
617620
CHECK_NE(req_wrap, nullptr);
618621
req_wrap->Init(syscall, dest, len, enc);
619-
int err = fn(env->event_loop(), req_wrap->req(), fn_args..., after);
620-
req_wrap->Dispatched();
622+
int err = req_wrap->Dispatch(fn, fn_args..., after);
621623
if (err < 0) {
622624
uv_fs_t* uv_req = req_wrap->req();
623625
uv_req->result = err;

src/pipe_wrap.cc

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -224,11 +224,10 @@ void PipeWrap::Connect(const FunctionCallbackInfo<Value>& args) {
224224

225225
ConnectWrap* req_wrap =
226226
new ConnectWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_PIPECONNECTWRAP);
227-
uv_pipe_connect(req_wrap->req(),
228-
&wrap->handle_,
229-
*name,
230-
AfterConnect);
231-
req_wrap->Dispatched();
227+
req_wrap->Dispatch(uv_pipe_connect,
228+
&wrap->handle_,
229+
*name,
230+
AfterConnect);
232231

233232
args.GetReturnValue().Set(0); // uv_pipe_connect() doesn't return errors.
234233
}

src/req_wrap-inl.h

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

src/req_wrap.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,28 @@ class ReqWrap : public AsyncWrap {
1717
v8::Local<v8::Object> object,
1818
AsyncWrap::ProviderType provider);
1919
inline ~ReqWrap() override;
20-
inline void Dispatched(); // Call this after the req has been dispatched.
20+
// Call this after the req has been dispatched, if that did not already
21+
// happen by using Dispatch().
22+
inline void Dispatched();
2123
T* req() { return &req_; }
2224
inline void Cancel();
2325

2426
static ReqWrap* from_req(T* req);
2527

28+
template <typename LibuvFunction, typename... Args>
29+
inline int Dispatch(LibuvFunction fn, Args... args);
30+
2631
private:
2732
friend class Environment;
2833
friend int GenDebugSymbols();
34+
template <typename ReqT, typename U>
35+
friend struct MakeLibuvRequestCallback;
36+
2937
ListNode<ReqWrap> req_wrap_queue_;
3038

39+
typedef void (*callback_t)();
40+
callback_t original_callback_ = nullptr;
41+
3142
protected:
3243
// req_wrap_queue_ needs to be at a fixed offset from the start of the class
3344
// because it is used by ContainerOf to calculate the address of the embedding

src/tcp_wrap.cc

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -287,11 +287,10 @@ void TCPWrap::Connect(const FunctionCallbackInfo<Value>& args) {
287287
AsyncHooks::DefaultTriggerAsyncIdScope trigger_scope(wrap);
288288
ConnectWrap* req_wrap =
289289
new ConnectWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_TCPCONNECTWRAP);
290-
err = uv_tcp_connect(req_wrap->req(),
291-
&wrap->handle_,
292-
reinterpret_cast<const sockaddr*>(&addr),
293-
AfterConnect);
294-
req_wrap->Dispatched();
290+
err = req_wrap->Dispatch(uv_tcp_connect,
291+
&wrap->handle_,
292+
reinterpret_cast<const sockaddr*>(&addr),
293+
AfterConnect);
295294
if (err)
296295
delete req_wrap;
297296
}
@@ -323,11 +322,10 @@ void TCPWrap::Connect6(const FunctionCallbackInfo<Value>& args) {
323322
AsyncHooks::DefaultTriggerAsyncIdScope trigger_scope(wrap);
324323
ConnectWrap* req_wrap =
325324
new ConnectWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_TCPCONNECTWRAP);
326-
err = uv_tcp_connect(req_wrap->req(),
327-
&wrap->handle_,
328-
reinterpret_cast<const sockaddr*>(&addr),
329-
AfterConnect);
330-
req_wrap->Dispatched();
325+
err = req_wrap->Dispatch(uv_tcp_connect,
326+
&wrap->handle_,
327+
reinterpret_cast<const sockaddr*>(&addr),
328+
AfterConnect);
331329
if (err)
332330
delete req_wrap;
333331
}

src/udp_wrap.cc

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -380,15 +380,14 @@ void UDPWrap::DoSend(const FunctionCallbackInfo<Value>& args, int family) {
380380
}
381381

382382
if (err == 0) {
383-
err = uv_udp_send(req_wrap->req(),
384-
&wrap->handle_,
385-
*bufs,
386-
count,
387-
reinterpret_cast<const sockaddr*>(&addr),
388-
OnSend);
383+
err = req_wrap->Dispatch(uv_udp_send,
384+
&wrap->handle_,
385+
*bufs,
386+
count,
387+
reinterpret_cast<const sockaddr*>(&addr),
388+
OnSend);
389389
}
390390

391-
req_wrap->Dispatched();
392391
if (err)
393392
delete req_wrap;
394393

0 commit comments

Comments
 (0)