Skip to content

Commit

Permalink
async-wrap: explicitly pass parent
Browse files Browse the repository at this point in the history
When instantiating a new AsyncWrap allow the parent AsyncWrap to be
passed. This is useful for cases like TCP incoming connections, so the
connection can be tied to the server receiving the connection.

Because the current architecture instantiates the *Wrap inside a
v8::FunctionCallback, the parent pointer is currently wrapped inside a
new v8::External every time and passed as an argument. This adds ~80ns
to instantiation time.

A future optimization would be to add the v8::External as the data field
when creating the v8::FunctionTemplate, change the pointer just before
making the call then NULL'ing it out afterwards. This adds enough code
complexity that it will not be attempted until the current approach
demonstrates it is a bottle neck.

PR-URL: nodejs/node-v0.x-archive#8110
Signed-off-by: Trevor Norris <trev.norris@gmail.com>
Reviewed-by: Fedor Indutny <fedor@indutny.com>
Reviewed-by: Alexis Campailla <alexis@janeasystems.com>
Reviewed-by: Julien Gilli <julien.gilli@joyent.com>
  • Loading branch information
trevnorris authored and piscisaureus committed Dec 9, 2014
1 parent c2ca873 commit 3f056f4
Show file tree
Hide file tree
Showing 12 changed files with 89 additions and 35 deletions.
3 changes: 2 additions & 1 deletion src/async-wrap-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ namespace node {

inline AsyncWrap::AsyncWrap(Environment* env,
v8::Handle<v8::Object> object,
ProviderType provider)
ProviderType provider,
AsyncWrap* parent)
: BaseObject(env, object),
provider_type_(provider) {
}
Expand Down
3 changes: 2 additions & 1 deletion src/async-wrap.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ class AsyncWrap : public BaseObject {

inline AsyncWrap(Environment* env,
v8::Handle<v8::Object> object,
ProviderType provider);
ProviderType provider,
AsyncWrap* parent = nullptr);

inline virtual ~AsyncWrap() override = default;

Expand Down
5 changes: 3 additions & 2 deletions src/handle_wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,9 @@ void HandleWrap::Close(const FunctionCallbackInfo<Value>& args) {
HandleWrap::HandleWrap(Environment* env,
Handle<Object> object,
uv_handle_t* handle,
AsyncWrap::ProviderType provider)
: AsyncWrap(env, object, provider),
AsyncWrap::ProviderType provider,
AsyncWrap* parent)
: AsyncWrap(env, object, provider, parent),
flags_(0),
handle__(handle) {
handle__->data = this;
Expand Down
3 changes: 2 additions & 1 deletion src/handle_wrap.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ class HandleWrap : public AsyncWrap {
HandleWrap(Environment* env,
v8::Handle<v8::Object> object,
uv_handle_t* handle,
AsyncWrap::ProviderType provider);
AsyncWrap::ProviderType provider,
AsyncWrap* parent = nullptr);
virtual ~HandleWrap() override;

private:
Expand Down
24 changes: 18 additions & 6 deletions src/pipe_wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

#include "pipe_wrap.h"

#include "async-wrap.h"
#include "env.h"
#include "env-inl.h"
#include "handle_wrap.h"
Expand All @@ -37,6 +38,7 @@ namespace node {
using v8::Boolean;
using v8::Context;
using v8::EscapableHandleScope;
using v8::External;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
Expand Down Expand Up @@ -74,12 +76,13 @@ uv_pipe_t* PipeWrap::UVHandle() {
}


Local<Object> PipeWrap::Instantiate(Environment* env) {
Local<Object> PipeWrap::Instantiate(Environment* env, AsyncWrap* parent) {
EscapableHandleScope handle_scope(env->isolate());
CHECK_EQ(false, env->pipe_constructor_template().IsEmpty());
Local<Function> constructor = env->pipe_constructor_template()->GetFunction();
CHECK_EQ(false, constructor.IsEmpty());
Local<Object> instance = constructor->NewInstance();
Local<Value> ptr = External::New(env->isolate(), parent);
Local<Object> instance = constructor->NewInstance(1, &ptr);
CHECK_EQ(false, instance.IsEmpty());
return handle_scope.Escape(instance);
}
Expand Down Expand Up @@ -147,15 +150,24 @@ void PipeWrap::New(const FunctionCallbackInfo<Value>& args) {
// normal function.
CHECK(args.IsConstructCall());
Environment* env = Environment::GetCurrent(args);
new PipeWrap(env, args.This(), args[0]->IsTrue());
if (args[0]->IsExternal()) {
void* ptr = args[0].As<External>()->Value();
new PipeWrap(env, args.This(), false, static_cast<AsyncWrap*>(ptr));
} else {
new PipeWrap(env, args.This(), args[0]->IsTrue(), nullptr);
}
}


PipeWrap::PipeWrap(Environment* env, Handle<Object> object, bool ipc)
PipeWrap::PipeWrap(Environment* env,
Handle<Object> object,
bool ipc,
AsyncWrap* parent)
: StreamWrap(env,
object,
reinterpret_cast<uv_stream_t*>(&handle_),
AsyncWrap::PROVIDER_PIPEWRAP) {
AsyncWrap::PROVIDER_PIPEWRAP,
parent) {
int r = uv_pipe_init(env->event_loop(), &handle_, ipc);
CHECK_EQ(r, 0); // How do we proxy this error up to javascript?
// Suggestion: uv_pipe_init() returns void.
Expand Down Expand Up @@ -214,7 +226,7 @@ void PipeWrap::OnConnection(uv_stream_t* handle, int status) {
}

// Instanciate the client javascript object and handle.
Local<Object> client_obj = Instantiate(env);
Local<Object> client_obj = Instantiate(env, pipe_wrap);

// Unwrap the client javascript object.
PipeWrap* wrap = Unwrap<PipeWrap>(client_obj);
Expand Down
8 changes: 6 additions & 2 deletions src/pipe_wrap.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#ifndef SRC_PIPE_WRAP_H_
#define SRC_PIPE_WRAP_H_

#include "async-wrap.h"
#include "env.h"
#include "stream_wrap.h"

Expand All @@ -31,13 +32,16 @@ class PipeWrap : public StreamWrap {
public:
uv_pipe_t* UVHandle();

static v8::Local<v8::Object> Instantiate(Environment* env);
static v8::Local<v8::Object> Instantiate(Environment* env, AsyncWrap* parent);
static void Initialize(v8::Handle<v8::Object> target,
v8::Handle<v8::Value> unused,
v8::Handle<v8::Context> context);

private:
PipeWrap(Environment* env, v8::Handle<v8::Object> object, bool ipc);
PipeWrap(Environment* env,
v8::Handle<v8::Object> object,
bool ipc,
AsyncWrap* parent);

static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Bind(const v8::FunctionCallbackInfo<v8::Value>& args);
Expand Down
21 changes: 14 additions & 7 deletions src/stream_wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,13 @@ void StreamWrap::Initialize(Handle<Object> target,
StreamWrap::StreamWrap(Environment* env,
Local<Object> object,
uv_stream_t* stream,
AsyncWrap::ProviderType provider)
: HandleWrap(env, object, reinterpret_cast<uv_handle_t*>(stream), provider),
AsyncWrap::ProviderType provider,
AsyncWrap* parent)
: HandleWrap(env,
object,
reinterpret_cast<uv_handle_t*>(stream),
provider,
parent),
stream_(stream),
default_callbacks_(this),
callbacks_(&default_callbacks_),
Expand Down Expand Up @@ -140,12 +145,14 @@ void StreamWrap::OnAlloc(uv_handle_t* handle,


template <class WrapType, class UVType>
static Local<Object> AcceptHandle(Environment* env, uv_stream_t* pipe) {
static Local<Object> AcceptHandle(Environment* env,
uv_stream_t* pipe,
AsyncWrap* parent) {
EscapableHandleScope scope(env->isolate());
Local<Object> wrap_obj;
UVType* handle;

wrap_obj = WrapType::Instantiate(env);
wrap_obj = WrapType::Instantiate(env, parent);
if (wrap_obj.IsEmpty())
return Local<Object>();

Expand Down Expand Up @@ -751,11 +758,11 @@ void StreamWrapCallbacks::DoRead(uv_stream_t* handle,

Local<Object> pending_obj;
if (pending == UV_TCP) {
pending_obj = AcceptHandle<TCPWrap, uv_tcp_t>(env, handle);
pending_obj = AcceptHandle<TCPWrap, uv_tcp_t>(env, handle, wrap());
} else if (pending == UV_NAMED_PIPE) {
pending_obj = AcceptHandle<PipeWrap, uv_pipe_t>(env, handle);
pending_obj = AcceptHandle<PipeWrap, uv_pipe_t>(env, handle, wrap());
} else if (pending == UV_UDP) {
pending_obj = AcceptHandle<UDPWrap, uv_udp_t>(env, handle);
pending_obj = AcceptHandle<UDPWrap, uv_udp_t>(env, handle, wrap());
} else {
CHECK_EQ(pending, UV_UNKNOWN_HANDLE);
}
Expand Down
3 changes: 2 additions & 1 deletion src/stream_wrap.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,8 @@ class StreamWrap : public HandleWrap {
StreamWrap(Environment* env,
v8::Local<v8::Object> object,
uv_stream_t* stream,
AsyncWrap::ProviderType provider);
AsyncWrap::ProviderType provider,
AsyncWrap* parent = nullptr);

~StreamWrap() {
if (!callbacks_gc_ && callbacks_ != &default_callbacks_) {
Expand Down
24 changes: 18 additions & 6 deletions src/tcp_wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ namespace node {
using v8::Boolean;
using v8::Context;
using v8::EscapableHandleScope;
using v8::External;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
Expand Down Expand Up @@ -70,12 +71,13 @@ static void NewTCPConnectWrap(const FunctionCallbackInfo<Value>& args) {
}


Local<Object> TCPWrap::Instantiate(Environment* env) {
Local<Object> TCPWrap::Instantiate(Environment* env, AsyncWrap* parent) {
EscapableHandleScope handle_scope(env->isolate());
CHECK_EQ(env->tcp_constructor_template().IsEmpty(), false);
Local<Function> constructor = env->tcp_constructor_template()->GetFunction();
CHECK_EQ(constructor.IsEmpty(), false);
Local<Object> instance = constructor->NewInstance();
Local<Value> ptr = External::New(env->isolate(), parent);
Local<Object> instance = constructor->NewInstance(1, &ptr);
CHECK_EQ(instance.IsEmpty(), false);
return handle_scope.Escape(instance);
}
Expand Down Expand Up @@ -166,16 +168,25 @@ void TCPWrap::New(const FunctionCallbackInfo<Value>& args) {
// normal function.
CHECK(args.IsConstructCall());
Environment* env = Environment::GetCurrent(args);
TCPWrap* wrap = new TCPWrap(env, args.This());
TCPWrap* wrap;
if (args.Length() == 0) {
wrap = new TCPWrap(env, args.This(), nullptr);
} else if (args[0]->IsExternal()) {
void* ptr = args[0].As<External>()->Value();
wrap = new TCPWrap(env, args.This(), static_cast<AsyncWrap*>(ptr));
} else {
UNREACHABLE();
}
CHECK(wrap);
}


TCPWrap::TCPWrap(Environment* env, Handle<Object> object)
TCPWrap::TCPWrap(Environment* env, Handle<Object> object, AsyncWrap* parent)
: StreamWrap(env,
object,
reinterpret_cast<uv_stream_t*>(&handle_),
AsyncWrap::PROVIDER_TCPWRAP) {
AsyncWrap::PROVIDER_TCPWRAP,
parent) {
int r = uv_tcp_init(env->event_loop(), &handle_);
CHECK_EQ(r, 0); // How do we proxy this error up to javascript?
// Suggestion: uv_tcp_init() returns void.
Expand Down Expand Up @@ -325,7 +336,8 @@ void TCPWrap::OnConnection(uv_stream_t* handle, int status) {

if (status == 0) {
// Instantiate the client javascript object and handle.
Local<Object> client_obj = Instantiate(env);
Local<Object> client_obj =
Instantiate(env, static_cast<AsyncWrap*>(tcp_wrap));

// Unwrap the client javascript object.
TCPWrap* wrap = Unwrap<TCPWrap>(client_obj);
Expand Down
5 changes: 3 additions & 2 deletions src/tcp_wrap.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,23 @@
#ifndef SRC_TCP_WRAP_H_
#define SRC_TCP_WRAP_H_

#include "async-wrap.h"
#include "env.h"
#include "stream_wrap.h"

namespace node {

class TCPWrap : public StreamWrap {
public:
static v8::Local<v8::Object> Instantiate(Environment* env);
static v8::Local<v8::Object> Instantiate(Environment* env, AsyncWrap* parent);
static void Initialize(v8::Handle<v8::Object> target,
v8::Handle<v8::Value> unused,
v8::Handle<v8::Context> context);

uv_tcp_t* UVHandle();

private:
TCPWrap(Environment* env, v8::Handle<v8::Object> object);
TCPWrap(Environment* env, v8::Handle<v8::Object> object, AsyncWrap* parent);
~TCPWrap();

static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
Expand Down
20 changes: 16 additions & 4 deletions src/udp_wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
namespace node {

using v8::Context;
using v8::EscapableHandleScope;
using v8::External;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
Expand Down Expand Up @@ -78,7 +80,7 @@ static void NewSendWrap(const FunctionCallbackInfo<Value>& args) {
}


UDPWrap::UDPWrap(Environment* env, Handle<Object> object)
UDPWrap::UDPWrap(Environment* env, Handle<Object> object, AsyncWrap* parent)
: HandleWrap(env,
object,
reinterpret_cast<uv_handle_t*>(&handle_),
Expand Down Expand Up @@ -140,7 +142,15 @@ void UDPWrap::Initialize(Handle<Object> target,
void UDPWrap::New(const FunctionCallbackInfo<Value>& args) {
CHECK(args.IsConstructCall());
Environment* env = Environment::GetCurrent(args);
new UDPWrap(env, args.This());
if (args.Length() == 0) {
new UDPWrap(env, args.This(), nullptr);
} else if (args[0]->IsExternal()) {
new UDPWrap(env,
args.This(),
static_cast<AsyncWrap*>(args[0].As<External>()->Value()));
} else {
UNREACHABLE();
}
}


Expand Down Expand Up @@ -423,10 +433,12 @@ void UDPWrap::OnRecv(uv_udp_t* handle,
}


Local<Object> UDPWrap::Instantiate(Environment* env) {
Local<Object> UDPWrap::Instantiate(Environment* env, AsyncWrap* parent) {
// If this assert fires then Initialize hasn't been called yet.
CHECK_EQ(env->udp_constructor_function().IsEmpty(), false);
return env->udp_constructor_function()->NewInstance();
EscapableHandleScope scope(env->isolate());
Local<Value> ptr = External::New(env->isolate(), parent);
return scope.Escape(env->udp_constructor_function()->NewInstance(1, &ptr));
}


Expand Down
5 changes: 3 additions & 2 deletions src/udp_wrap.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#ifndef SRC_UDP_WRAP_H_
#define SRC_UDP_WRAP_H_

#include "async-wrap.h"
#include "env.h"
#include "handle_wrap.h"
#include "req_wrap.h"
Expand Down Expand Up @@ -53,11 +54,11 @@ class UDPWrap: public HandleWrap {
static void SetBroadcast(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetTTL(const v8::FunctionCallbackInfo<v8::Value>& args);

static v8::Local<v8::Object> Instantiate(Environment* env);
static v8::Local<v8::Object> Instantiate(Environment* env, AsyncWrap* parent);
uv_udp_t* UVHandle();

private:
UDPWrap(Environment* env, v8::Handle<v8::Object> object);
UDPWrap(Environment* env, v8::Handle<v8::Object> object, AsyncWrap* parent);

static void DoBind(const v8::FunctionCallbackInfo<v8::Value>& args,
int family);
Expand Down

0 comments on commit 3f056f4

Please sign in to comment.