From cecb08f0c828dd5da8e4c6f8414bb1f9c778319e Mon Sep 17 00:00:00 2001 From: James M Snell Date: Fri, 10 Apr 2020 19:42:37 -0700 Subject: [PATCH] src: add AliasedStruct utility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For http2 (and eventually QUIC) we have a struct that is backed by a v8::BackingStore and exposed to the JavaScript side as an ArrayBuffer and TypedArray. This is similar to AliasedBuffer except that it is fronted by a struct on the C++ side. ```c++ struct foo { uint32_t ex1; uint32_t ex2; }; AliasedStruct foo_; foo_->ex1 = 1; foo_->ex2 = 2; foo_.GetArrayBuffer(); ``` Signed-off-by: James M Snell PR-URL: https://github.com/nodejs/node/pull/32778 Reviewed-By: Anna Henningsen Reviewed-By: Gerhard Stöbich --- node.gyp | 2 ++ src/aliased_struct-inl.h | 54 ++++++++++++++++++++++++++++++++++ src/aliased_struct.h | 63 ++++++++++++++++++++++++++++++++++++++++ src/node_http2.cc | 12 ++------ src/node_http2.h | 4 +-- 5 files changed, 124 insertions(+), 11 deletions(-) create mode 100644 src/aliased_struct-inl.h create mode 100644 src/aliased_struct.h diff --git a/node.gyp b/node.gyp index d82c4d89ffe253..c3ffd2ff2147e2 100644 --- a/node.gyp +++ b/node.gyp @@ -631,6 +631,8 @@ 'src/uv.cc', # headers to make for a more pleasant IDE experience 'src/aliased_buffer.h', + 'src/aliased_struct.h', + 'src/aliased_struct-inl.h', 'src/async_wrap.h', 'src/async_wrap-inl.h', 'src/base_object.h', diff --git a/src/aliased_struct-inl.h b/src/aliased_struct-inl.h new file mode 100644 index 00000000000000..17d5ff58097e22 --- /dev/null +++ b/src/aliased_struct-inl.h @@ -0,0 +1,54 @@ +#ifndef SRC_ALIASED_STRUCT_INL_H_ +#define SRC_ALIASED_STRUCT_INL_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "aliased_struct.h" +#include "v8.h" +#include + +namespace node { + +template +template +AliasedStruct::AliasedStruct(v8::Isolate* isolate, Args&&... args) + : isolate_(isolate) { + const v8::HandleScope handle_scope(isolate); + + store_ = v8::ArrayBuffer::NewBackingStore(isolate, sizeof(T)); + ptr_ = new (store_->Data()) T(std::forward(args)...); + DCHECK_NOT_NULL(ptr_); + + v8::Local buffer = v8::ArrayBuffer::New(isolate, store_); + buffer_ = v8::Global(isolate, buffer); +} + +template +AliasedStruct::AliasedStruct(const AliasedStruct& that) + : AliasedStruct(that.isolate_, *that) {} + +template +AliasedStruct& AliasedStruct::operator=( + AliasedStruct&& that) noexcept { + this->~AliasedStruct(); + isolate_ = that.isolate_; + store_ = that.store_; + ptr_ = that.ptr_; + + buffer_ = std::move(that.buffer_); + + that.ptr_ = nullptr; + that.store_.reset(); + return *this; +} + +template +AliasedStruct::~AliasedStruct() { + if (ptr_ != nullptr) ptr_->~T(); +} + +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_ALIASED_STRUCT_INL_H_ diff --git a/src/aliased_struct.h b/src/aliased_struct.h new file mode 100644 index 00000000000000..e4df393f4985a3 --- /dev/null +++ b/src/aliased_struct.h @@ -0,0 +1,63 @@ +#ifndef SRC_ALIASED_STRUCT_H_ +#define SRC_ALIASED_STRUCT_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "node_internals.h" +#include "v8.h" +#include + +namespace node { + +// AliasedStruct is a utility that allows uses a V8 Backing Store +// to be exposed to the C++/C side as a struct and to the +// JavaScript side as an ArrayBuffer to efficiently share +// data without marshalling. It is similar in nature to +// AliasedBuffer. +// +// struct Foo { int x; } +// +// AliasedStruct foo; +// foo->x = 1; +// +// Local ab = foo.GetArrayBuffer(); +template +class AliasedStruct final { + public: + template + explicit AliasedStruct(v8::Isolate* isolate, Args&&... args); + + inline AliasedStruct(const AliasedStruct& that); + + inline ~AliasedStruct(); + + inline AliasedStruct& operator=(AliasedStruct&& that) noexcept; + + v8::Local GetArrayBuffer() const { + return buffer_.Get(isolate_); + } + + const T* Data() const { return ptr_; } + + T* Data() { return ptr_; } + + const T& operator*() const { return *ptr_; } + + T& operator*() { return *ptr_; } + + const T* operator->() const { return ptr_; } + + T* operator->() { return ptr_; } + + private: + v8::Isolate* isolate_; + std::shared_ptr store_; + T* ptr_; + v8::Global buffer_; +}; + +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_ALIASED_STRUCT_H_ diff --git a/src/node_http2.cc b/src/node_http2.cc index d884be3e7553fc..7477bfbb6d8c10 100644 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -1,4 +1,5 @@ #include "aliased_buffer.h" +#include "aliased_struct-inl.h" #include "debug_utils-inl.h" #include "memory_tracker-inl.h" #include "node.h" @@ -18,7 +19,6 @@ namespace node { using v8::Array; using v8::ArrayBuffer; using v8::ArrayBufferView; -using v8::BackingStore; using v8::Boolean; using v8::Context; using v8::Float64Array; @@ -471,6 +471,7 @@ Http2Session::Http2Session(Http2State* http2_state, Local wrap, nghttp2_session_type type) : AsyncWrap(http2_state->env(), wrap, AsyncWrap::PROVIDER_HTTP2SESSION), + js_fields_(http2_state->env()->isolate()), session_type_(type), http2_state_(http2_state) { MakeWeak(); @@ -518,14 +519,8 @@ Http2Session::Http2Session(Http2State* http2_state, outgoing_storage_.reserve(1024); outgoing_buffers_.reserve(32); - // Make the js_fields_ property accessible to JS land. - js_fields_store_ = - ArrayBuffer::NewBackingStore(env()->isolate(), sizeof(SessionJSFields)); - js_fields_ = new(js_fields_store_->Data()) SessionJSFields; - - Local ab = ArrayBuffer::New(env()->isolate(), js_fields_store_); Local uint8_arr = - Uint8Array::New(ab, 0, kSessionUint8FieldCount); + Uint8Array::New(js_fields_.GetArrayBuffer(), 0, kSessionUint8FieldCount); USE(wrap->Set(env()->context(), env()->fields_string(), uint8_arr)); } @@ -536,7 +531,6 @@ Http2Session::~Http2Session() { // current_nghttp2_memory_ check passes. session_.reset(); CHECK_EQ(current_nghttp2_memory_, 0); - js_fields_->~SessionJSFields(); } std::string Http2Session::diagnostic_name() const { diff --git a/src/node_http2.h b/src/node_http2.h index f9a0cae85ff2ae..1e5f99acfacec9 100644 --- a/src/node_http2.h +++ b/src/node_http2.h @@ -7,6 +7,7 @@ #include #include "nghttp2/nghttp2.h" +#include "aliased_struct.h" #include "node_http2_state.h" #include "node_http_common.h" #include "node_mem.h" @@ -823,8 +824,7 @@ class Http2Session : public AsyncWrap, Nghttp2SessionPointer session_; // JS-accessible numeric fields, as indexed by SessionUint8Fields. - SessionJSFields* js_fields_ = nullptr; - std::shared_ptr js_fields_store_; + AliasedStruct js_fields_; // The session type: client or server nghttp2_session_type session_type_;