李恒道 发表于 2023-2-9 00:36:00

nodejs源码 ConnectWrap的Dispatch调用分析

我们可以看一段代码
```js
ConnectWrap* req_wrap =new ConnectWrap(env,req_wrap_obj, ...);
req_wrap->Dispatch(uv_tcp_connect,
                     &wrap->handle_,
                     reinterpret_cast<const sockaddr*>(&addr),
                     AfterConnect);
```
这里调用了Dispatch是属于ReqWrap类的
```js
template <typename T>
template <typename LibuvFunction, typename... Args>
int ReqWrap<T>::Dispatch(LibuvFunction fn, Args... args) {
Dispatched();
CallLibuvFunction<T, LibuvFunction>::Call(
      fn,
      env()->event_loop(),
      req(),
      MakeLibuvRequestCallback<T, Args>::For(this, args)...);
}
```
Dispatched是一个设置data,不用管,可以看到这里调用了CallLibuvFunction<T, LibuvFunction>::Call
我们去看看看Call的代码
```js
// 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;
}
};
```
因为我们调用的uv_tcp_connect的函数表达式是
int uv_tcp_connect(uv_connect_t* req,
                     uv_tcp_t* handle,
                     const struct sockaddr* addr,
                     uv_connect_cb cb)
所以应该执行的是
```js
// 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...);
}
};
```
所以我们调用的是
uv_tcp_connect(req(), MakeLibuvRequestCallback<T, Args>::For(this, args)...)
args收集是
```js
template <typename LibuvFunction, typename... Args>
int ReqWrap<T>::Dispatch(LibuvFunction fn, Args... args)
```
也就是Dispatch除了fn的函数那就是

三个
我们拼接上得到了
```
uv_tcp_connect(req(), MakeLibuvRequestCallback<T, Args>::For(this, &wrap->handle_,
reinterpret_cast<const sockaddr*>(&addr),
AfterConnect)...)
```

现在我们看看MakeLibuvRequestCallback是什么
```js
template <typename ReqT, typename T>
struct MakeLibuvRequestCallback {
static T For(ReqWrap<ReqT>* req_wrap, T v) {
    return v;
}
};

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);
    F original_callback = reinterpret_cast<F>(req_wrap->original_callback_);
    original_callback(req, args...);
}
static F For(ReqWrap<ReqT>* req_wrap, F v) {
    req_wrap->original_callback_ = reinterpret_cast<typename ReqWrap<ReqT>::callback_t>(v);
    return Wrapper;
}
};

```
可以看到分为两种情况
如果传入的第二个参数不是函数,则将参数直接原封不动传出去,如果是函数,则进行包裹返回一个函数
我们这里第二个参数是&wrap->handle_
一个handle
所以会透传,那么最后
```
uv_tcp_connect(req(), MakeLibuvRequestCallback<T, Args>::For(this, &wrap->handle_,
reinterpret_cast<const sockaddr*>(&addr),
AfterConnect)...)
```
会变成
```js
uv_tcp_connect(
req(),
&wrap->handle_,
reinterpret_cast<const sockaddr*>(&addr),
AfterConnect
)
```
刚好符合tcp的调用头
````js
    int uv_tcp_connect(uv_connect_t* req,
                     uv_tcp_t* handle,
                     const struct sockaddr* addr,
                     uv_connect_cb cb) {
      // ...
      return uv__tcp_connect(req, handle, addr, addrlen, cb);
    }
```
页: [1]
查看完整版本: nodejs源码 ConnectWrap的Dispatch调用分析