Skip to content

Commit

Permalink
src: add AliasedStruct utility
Browse files Browse the repository at this point in the history
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_;

foo_->ex1 = 1;
foo_->ex2 = 2;

foo_.GetArrayBuffer();
```

Signed-off-by: James M Snell <jasnell@gmail.com>

PR-URL: #32778
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Gerhard Stöbich <deb2001-github@yahoo.de>
  • Loading branch information
jasnell authored and addaleax committed Apr 14, 2020
1 parent 81c03bc commit cecb08f
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 11 deletions.
2 changes: 2 additions & 0 deletions node.gyp
Expand Up @@ -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',
Expand Down
54 changes: 54 additions & 0 deletions 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 <memory>

namespace node {

template <typename T>
template <typename... Args>
AliasedStruct<T>::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>(args)...);
DCHECK_NOT_NULL(ptr_);

v8::Local<v8::ArrayBuffer> buffer = v8::ArrayBuffer::New(isolate, store_);
buffer_ = v8::Global<v8::ArrayBuffer>(isolate, buffer);
}

template <typename T>
AliasedStruct<T>::AliasedStruct(const AliasedStruct& that)
: AliasedStruct(that.isolate_, *that) {}

template <typename T>
AliasedStruct<T>& AliasedStruct<T>::operator=(
AliasedStruct<T>&& 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 <typename T>
AliasedStruct<T>::~AliasedStruct() {
if (ptr_ != nullptr) ptr_->~T();
}

} // namespace node

#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

#endif // SRC_ALIASED_STRUCT_INL_H_
63 changes: 63 additions & 0 deletions 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 <memory>

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;
// foo->x = 1;
//
// Local<ArrayBuffer> ab = foo.GetArrayBuffer();
template <typename T>
class AliasedStruct final {
public:
template <typename... Args>
explicit AliasedStruct(v8::Isolate* isolate, Args&&... args);

inline AliasedStruct(const AliasedStruct& that);

inline ~AliasedStruct();

inline AliasedStruct& operator=(AliasedStruct&& that) noexcept;

v8::Local<v8::ArrayBuffer> 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<v8::BackingStore> store_;
T* ptr_;
v8::Global<v8::ArrayBuffer> buffer_;
};

} // namespace node

#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

#endif // SRC_ALIASED_STRUCT_H_
12 changes: 3 additions & 9 deletions 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"
Expand All @@ -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;
Expand Down Expand Up @@ -471,6 +471,7 @@ Http2Session::Http2Session(Http2State* http2_state,
Local<Object> 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();
Expand Down Expand Up @@ -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<ArrayBuffer> ab = ArrayBuffer::New(env()->isolate(), js_fields_store_);
Local<Uint8Array> uint8_arr =
Uint8Array::New(ab, 0, kSessionUint8FieldCount);
Uint8Array::New(js_fields_.GetArrayBuffer(), 0, kSessionUint8FieldCount);
USE(wrap->Set(env()->context(), env()->fields_string(), uint8_arr));
}

Expand All @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions src/node_http2.h
Expand Up @@ -7,6 +7,7 @@
#include <cstdint>
#include "nghttp2/nghttp2.h"

#include "aliased_struct.h"
#include "node_http2_state.h"
#include "node_http_common.h"
#include "node_mem.h"
Expand Down Expand Up @@ -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<v8::BackingStore> js_fields_store_;
AliasedStruct<SessionJSFields> js_fields_;

// The session type: client or server
nghttp2_session_type session_type_;
Expand Down

0 comments on commit cecb08f

Please sign in to comment.