Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

http2, async-wrap: introduce AliasedBuffer class #15077

Closed

Conversation

mike-kaufman
Copy link

This change introduces an AliasedBuffer class and updates async-wrap
and http2 to use this class.

A common technique to optimize performance is to create a native buffer
and then map that native buffer to user space via JS array. The runtime
can efficiently write to the native buffer without having to route
though JS, and the values being written are accessible from user space.

While efficient, this technique allows modifications to user
space memory w/out going through JS type system APIs, effectively
bypassing any monitoring the JS VM has in place to track program state
modifications. The result is that monitors have an incorrect view
of program state.

The AliasedBuffer class provides a future placeholder where this
technique can be used, but writes can still be observed. To achieve
this, the node-chakra-core fork will add in appropriate tracking logic
in the AliasedBuffer's SetValue() method. Going forward, this class can
evolve to support more sophisticated mechanisms if necessary.

  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included (tests added, happy to run benchmarks, just lmk)
  • documentation is changed or added
  • commit message follows commit guidelines
Affected core subsystem(s)

async-wrap, http2

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. labels Aug 29, 2017
@mike-kaufman
Copy link
Author

/cc @jasnell, @addaleax, @mrkmarron.

*/
inline void SetValue(const int index, NativeT value) {
// if (index >= count_) {
// TODO(@mike-kaufman): Assert? Fail fast?
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wasn't clear if I should do anything here. Please let me know if there's existing precedent around such checks.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CHECK is likely appropriate. /cc @bnoordhuis

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added a CHECK.

*/
inline const NativeT GetValue(const int index) const {
// if (index >= count_) {
// TODO(@mike-kaufman): Assert? Fail fast?
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similiar here...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed now with a CHECK.

free(this->buffer_);
}
// TODO(@mike-kaufman): what do we do about freeing the
// v8::Global<ArrayBuffer> & the v8::Global<U>
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not clear to me if I need to do anything to manage lifecycle of a v8::Global? Do I need to call reset()?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That I'm unsure about. @addaleax or @bnoordhuis may know.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A Global<> does need to be reset.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed. calling .Reset() on the v8::Global.

@@ -0,0 +1,116 @@
#ifndef SRC_NODE_HTTP2_STATE_H_
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to move most of the stuff here out of node_http2.cc because http2_state was forward declaration, and I was seeing a compiler warning about calling delete on a forward declaration. I initially tried to introduce a #include "node_http2.h" into env.h, but that was causing cycles.

// v8::MaybeLocal<v8::Number> v3 = v2->ToNumber();
// v8::Local<v8::Number> v4 = v3.ToLocalChecked();
// NativeT actualValue = *(reinterpret_cast<NativeT*>(&v4->Value()));
// EXPECT_TRUE((NativeT)(v4->Value()) == oracle[i]);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to validate the value being read from the JS api is the same as the native type value, but I couldn't get this to work. Is there an easy way to do this? Some casting magic I need to do?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not that I'm aware of.


// allocate native buffer
this->buffer_ = reinterpret_cast<NativeT*>(
calloc(count, sizeof(NativeT)));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this follow the same pattern as MaybeStackBuffer... or even make use of that to avoid the calloc where possible?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could, but I'm not following why. Can you help me understand your concerns w/ using calloc? I was trying to be consistent w/ original buffers that were alloc'd on heap.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consistency would be the main reason but it's certainly not critical. I don't think the performance difference is going to be enough to worry about.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm now using node::UncheckedCalloc. Is using MaybeStackBuffer still something to investigate?

@jasnell
Copy link
Member

jasnell commented Aug 29, 2017

Very good. I'll give it a thorough review and sign off later but I'd like to get @addaleax, @bnoordhuis and @trevnorris to take a look also

@mscdex mscdex added async_wrap http2 Issues or PRs related to the http2 subsystem. labels Aug 29, 2017
@mscdex
Copy link
Contributor

mscdex commented Aug 29, 2017

What kind of performance impact does this have?

@mike-kaufman
Copy link
Author

What kind of performance impact does this have?

@mscdex - what tests would you suggest I run to measure this?

I ran acmeair-nodejs benchmark, comparing my local master (at this commit) with my changes.

Results didn't show any meaningful impact:

master:

    "numRequests": 533758,
    "elapsedTime": 209787,
    "requestsPerSecond": 2544.2853942331985,
    "averageLatency": 9.682236144469966,

with my change:

    "numRequests": 536971,
    "elapsedTime": 209800,
    "requestsPerSecond": 2559.4423260247854,
    "averageLatency": 9.625374554677999,

Note that intention with this change was to have the GetValue()/SetValue() methods inline and effectively result in 0/minimal overhead vs previous code (i.e., directly accessing offsets in the native buffer). Now, the use the overloaded operator [] may add some overhead here (it is a bit more complex).

I'm happy to run a better set of tests to measure impact here, I just don't know what they are.

@jasnell
Copy link
Member

jasnell commented Aug 29, 2017

@mscdex ... with regards to performance, it's important just to note that this is largely just a formalization of a pattern we were already using. I wouldn't expect the performance to be much different than what we see currently, tho I do suspect the likelihood of bugs to go way down ;-)

Copy link
Contributor

@trevnorris trevnorris left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The patch doesn't follow general whitespace and line break rules that the remainder of core follows. I haven't run the linter to see if they violate the current rules, but regardless they should align with what node already uses.

Overall I'm -1 on this change. The abstraction adds more code than it removes, and it substantially adds more complexity. This isn't a pattern that we need to use much, and I'm willing to bet that it's one we won't need to use much in the future.

freeBuffer_(true) {
const v8::HandleScope handle_scope(this->isolate_);

const int sizeInBytes = sizeof(NativeT) * count;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sizeof() returns a size_t.


// allocate native buffer
this->buffer_ = reinterpret_cast<NativeT*>(
calloc(count, sizeof(NativeT)));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two problems here. 1) please use node::UncheckedCalloc() instead of using calloc() directly. 2) You're not checking if this->buffer_ == nullptr afterwards.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @trevnorris, will do. Is there an example of fail-fast behavior when out of memory occurs? e.g., is there a specific error code to return?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. I tend to completely forget that we have those Calloc wrappers there.

v8::Uint8Array>& backingBuffer) :
isolate_(isolate),
count_(count),
byte_offset_(byte_offset),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A CHECK() to verify that byte_offset_ is aligned would be helpful.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fwiw, that check isn't strictly necessary because the V8 TypedArray constructor checks it anyway

free(this->buffer_);
}
// TODO(@mike-kaufman): what do we do about freeing the
// v8::Global<ArrayBuffer> & the v8::Global<U>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A Global<> does need to be reset.

@jasnell
Copy link
Member

jasnell commented Aug 30, 2017

@trevnorris... see here for some of the background leading into this: nodejs/node-chakracore#372

The short version is, the way the code is written now, it causes some headaches for the Chakra folks that this revised method is intended to ease. Given the problem, I think this is the best way to address it but I'd certainly love to have you review and comment on that! :-)

@mike-kaufman
Copy link
Author

@trevnorris -

The patch doesn't follow general whitespace and line break rules that the remainder of core follows.

Linter is clean on osx for me. Can you point out specifics? Or is there a style guide I'm missing?

Overall I'm -1 on this change. The abstraction adds more code than it removes, and it substantially adds more complexity.

The key thing here is this simplifies cases where we need to observe writes to that program state. e.g., take a look at https://github.com/nodejs/node-chakracore/pull/252/files. The code here to "mark" these writes is scattered around in a way that is prone to breakage & merge conflicts. The AliasedBuffer change lets us consolidate this at a single point in a maintainable/sane way.

Happy to discuss other approaches here if you have ideas.

Copy link
Member

@jasnell jasnell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, would like to make sure @addaleax and @trevnorris are good on it tho

Copy link
Member

@addaleax addaleax left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, left a few style nits that would bring this a little more in line with our usual style, but nothing blocking :)

this->buffer_ = UncheckedCalloc<NativeT>(count);
if (this->buffer_ == nullptr) {
ABORT();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At this point you might as well use the checked kind of alloc? ;)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed.


// allocate v8 ArrayBuffer
v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(
this->isolate_, this->buffer_, sizeInBytes);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tiny style nit: 4 spaces indentation for statement continuations (and we don't usually do this->)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added 4 spaces for continuations & removed this-> qualifiers.


// allocate v8 TypedArray
v8::Local<V8T> jsArray = V8T::New(ab, this->byte_offset_, count);
this->jsArray_ = v8::Global<V8T>(isolate, jsArray);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tiny style nit: snake_case (js_array, js_array_) for variables/member fields

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

snake cased vars & memebers. LMK if I missed anything.

v8::Uint8Array>& backingBuffer) :
isolate_(isolate),
count_(count),
byte_offset_(byte_offset),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fwiw, that check isn't strictly necessary because the V8 TypedArray constructor checks it anyway

return *this;
}

operator NativeT() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be const, fwiw

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

src/env-inl.h Outdated
@@ -457,7 +462,8 @@ inline double Environment::trigger_id() {
}

inline double Environment::get_init_trigger_id() {
double* uid_fields = async_hooks()->uid_fields();
AliasedBuffer<double, v8::Float64Array> uid_fields =
async_hooks()->uid_fields();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could use a reference (AliasedBuffer<double, v8::Float64Array>&) instead, right? I think that would allow you to get rid of AliasedBuffer's copy constructor (and mark that as deleted), which seems like a good idea to me, I think

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, you're right. I'll fix...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@@ -149,7 +93,8 @@ ssize_t Http2Session::OnCallbackPadding(size_t frameLen,
Context::Scope context_scope(context);

if (object()->Has(context, env()->ongetpadding_string()).FromJust()) {
uint32_t* buffer = env()->http2_state_buffer()->padding_buffer;
AliasedBuffer<uint32_t, v8::Uint32Array> buffer =
env()->http2_state()->padding_buffer;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of these could be references as well, right?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

// read through the API
for (size_t i = 0; i < size; i++) {
NativeT v1 = (NativeT)aliasedBuffer[i];
NativeT v2 = (NativeT)aliasedBuffer.GetValue(i);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style nit: Can you use static_cast for these?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed cast, wasn't necessary.

// NativeT actualValue = *(reinterpret_cast<NativeT*>(&v4->Value()));
// EXPECT_TRUE((NativeT)(v4->Value()) == oracle[i]);
// TODO(@mike-kaufman) figure out how to compare the value in the v8 array
// Expect_TRUE(*v.ToLocal() == v8NumberCreateFunction(oracle[i]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure what this code would mean, if I'm honest. Is this code for checking object identity between number values?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to validate that the value read through JS APIs was the same value as what was written to the native array. I just never found the right set of APIs and/or casts to make this happen. Was hoping there was something straightforward that I was missing.

EXPECT_TRUE(v2->IsNumber());
// v8::MaybeLocal<v8::Number> v3 = v2->ToNumber();
// v8::Local<v8::Number> v4 = v3.ToLocalChecked();
// NativeT actualValue = *(reinterpret_cast<NativeT*>(&v4->Value()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this could work, you're taking a pointer to a stack-allocated return value and casting it around`.

NativeT actualValue = static_cast<NativeT>(&v4->Value());?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll play around with that...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, got this to work. Think I was seeing seg fault earlier because I didn't pass context to ToNumber

@addaleax
Copy link
Member

Overall I'm -1 on this change. The abstraction adds more code than it removes, and it substantially adds more complexity. This isn't a pattern that we need to use much, and I'm willing to bet that it's one we won't need to use much in the future.

@trevnorris This pattern solves a real problem, so if you have other solutions, feel free to voice them (but not doing so is kind of unproductive). Also, I would argue that the encapsulation that comes with this does remove complexity; it makes it more obvious to readers that a write to these ArrayBuffers is not just a memory write, but actually changes state for JS.

@mike-kaufman
Copy link
Author

Thanks @addaleax for the review! I think I addressed all the feedback & rebased off of master. LMK if I missed anything. I didn't squash in case anyone wants to look at diffs, but I can do that.

@jasnell
Copy link
Member

jasnell commented Aug 31, 2017

@trevnorris ... I absolutely have to agree with @addaleax on this. This addresses a real problem even if we don't use this pattern extensively. Please reconsider your -1

@addaleax
Copy link
Member

addaleax commented Sep 3, 2017

@mike-kaufman Just occurred to me that a proper solution for the delete http2_state; problem might be to move the Environment constructor and destructor definition out of the header file and into env.cc, if you want to explore that. (You don’t need to, since you already figured out sth here)

@jasnell
Copy link
Member

jasnell commented Sep 5, 2017

ping @trevnorris

* Get value at position index
*/
inline const NativeT GetValue(const size_t index) const {
CHECK_LT(index, count_);
Copy link
Member

@addaleax addaleax Sep 5, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@trevnorris I’m pretty sure the reported performance difference is due to this check, it’s the only visible difference in the generated machine code. I’d suggest wrapping it in #if defined(DEBUG) && DEBUG?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

confirmed. that is where the performance hit is coming from. wrapping it sounds good

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this and the SetValue check are now in debug-only builds.

* Set position index to given value.
*/
inline void SetValue(const size_t index, NativeT value) {
CHECK_LT(index, count_);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(same goes for this one, I assume)

@mike-kaufman
Copy link
Author

Sorry for the delayed response here, I've been off the grid for the last week or so. I'll address latest feedback shortly.

@trevnorris -

Can you elaborate on how this "future placeholder" will be used? I don't see how this PR does anything to help notification of state change when JS writes to memory.

Short term, in the chakra fork, we'll add some logic in the SetValue method that will track anytime this state is modified. Long term is TBD. If need for something more general arises, you could have an API that allows callbacks to be registered that will get fired every time SetValue is called. Irrespective of the details, with this change there's now a consolidated place where these writes occur, namely SetValue.

@mike-kaufman
Copy link
Author

I think I addressed the latest feedback. Please let me know if I missed anything.

@jasnell -

How about we separate concerns here. Let's pull the AsyncWrap related changes out of this PR and ,move them into a separate PR.

I'm happy to do this, but I feel like we're close here? LMK if not the case.

@jasnell
Copy link
Member

jasnell commented Sep 12, 2017

As long as @trevnorris and @addaleax are happy, I'm good.

buffer_ = reinterpret_cast<NativeT*>(
const_cast<uint8_t*>(backing_buffer.GetNativeBuffer() + byte_offset));

v8::Local<v8::ArrayBuffer> ab = backing_buffer.GetArrayBuffer();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it be worth it to assign backing_buffer.GetNativeBuffer() to a variable at the top, since it's used three times here? (not a blocker)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed. (assuming you meant GetArrayBuffer()).

inline const NativeT GetValue(const size_t index) const {
#if defined(DEBUG) && DEBUG
CHECK_LT(index, count_);
#endif
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit (non-blocking): more common indentation is to have no whitespace before the #if.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Copy link
Contributor

@trevnorris trevnorris left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

two questions/suggestions, but nothing technically blocking. LGTM.

Mike Kaufman and others added 4 commits September 12, 2017 17:03
This change introduces an AliasedBuffer class and updates asytnc-wrap
and http2 to use this class.

A common technique to optimize performance is to create a native buffer
and then map that native buffer to user space via JS array.  The runtime
can efficiently write to the native buffer without having to route
though JS, and the values being written are accessible from user space.

While efficient, this technique allows modifications to user
space memory w/out going through JS type system APIs, effectively
bypassing any monitoring the JS VM has in place to track program state
modifications.  The result is that monitors have an incorrect view
of prorgram state.

The AliasedBuffer class provides a future placeholder where this
technique can be used, but writes can still be observed.  To achieve
this, the node-chakra-core fork will add in appropriate tracking logic
in the AliasedBuffer's SetValue() method. Going forward, this class can
evolve to support more sophisticated mechanisms if necessary.
…r/variable names, adding const, some other stuff...
@addaleax
Copy link
Member

Fresh CI: https://ci.nodejs.org/job/node-test-commit/12329/

This should be ready.

@jasnell
Copy link
Member

jasnell commented Sep 13, 2017

Still LGTM

@mike-kaufman
Copy link
Author

Great! Thanks @addaleax, @trevnorris, @jasnell! Anything else I need to do here? When can this get merged in?

@jasnell
Copy link
Member

jasnell commented Sep 13, 2017

If someone else doesn't beat me to it, I'll be merging it in later on this morning.

@addaleax
Copy link
Member

addaleax commented Sep 13, 2017

Landed in 35a526c

@addaleax addaleax closed this Sep 13, 2017
addaleax pushed a commit that referenced this pull request Sep 13, 2017
This change introduces an AliasedBuffer class and updates asytnc-wrap
and http2 to use this class.

A common technique to optimize performance is to create a native buffer
and then map that native buffer to user space via JS array.  The runtime
can efficiently write to the native buffer without having to route
though JS, and the values being written are accessible from user space.

While efficient, this technique allows modifications to user
space memory w/out going through JS type system APIs, effectively
bypassing any monitoring the JS VM has in place to track program state
modifications.  The result is that monitors have an incorrect view
of prorgram state.

The AliasedBuffer class provides a future placeholder where this
technique can be used, but writes can still be observed.  To achieve
this, the node-chakra-core fork will add in appropriate tracking logic
in the AliasedBuffer's SetValue() method. Going forward, this class can
evolve to support more sophisticated mechanisms if necessary.

PR-URL: #15077
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
addaleax pushed a commit to addaleax/ayo that referenced this pull request Sep 17, 2017
This change introduces an AliasedBuffer class and updates asytnc-wrap
and http2 to use this class.

A common technique to optimize performance is to create a native buffer
and then map that native buffer to user space via JS array.  The runtime
can efficiently write to the native buffer without having to route
though JS, and the values being written are accessible from user space.

While efficient, this technique allows modifications to user
space memory w/out going through JS type system APIs, effectively
bypassing any monitoring the JS VM has in place to track program state
modifications.  The result is that monitors have an incorrect view
of prorgram state.

The AliasedBuffer class provides a future placeholder where this
technique can be used, but writes can still be observed.  To achieve
this, the node-chakra-core fork will add in appropriate tracking logic
in the AliasedBuffer's SetValue() method. Going forward, this class can
evolve to support more sophisticated mechanisms if necessary.

PR-URL: nodejs/node#15077
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
jasnell pushed a commit that referenced this pull request Sep 20, 2017
This change introduces an AliasedBuffer class and updates asytnc-wrap
and http2 to use this class.

A common technique to optimize performance is to create a native buffer
and then map that native buffer to user space via JS array.  The runtime
can efficiently write to the native buffer without having to route
though JS, and the values being written are accessible from user space.

While efficient, this technique allows modifications to user
space memory w/out going through JS type system APIs, effectively
bypassing any monitoring the JS VM has in place to track program state
modifications.  The result is that monitors have an incorrect view
of prorgram state.

The AliasedBuffer class provides a future placeholder where this
technique can be used, but writes can still be observed.  To achieve
this, the node-chakra-core fork will add in appropriate tracking logic
in the AliasedBuffer's SetValue() method. Going forward, this class can
evolve to support more sophisticated mechanisms if necessary.

PR-URL: #15077
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Qard pushed a commit to Qard/ayo that referenced this pull request Sep 21, 2017
This change introduces an AliasedBuffer class and updates asytnc-wrap
and http2 to use this class.

A common technique to optimize performance is to create a native buffer
and then map that native buffer to user space via JS array.  The runtime
can efficiently write to the native buffer without having to route
though JS, and the values being written are accessible from user space.

While efficient, this technique allows modifications to user
space memory w/out going through JS type system APIs, effectively
bypassing any monitoring the JS VM has in place to track program state
modifications.  The result is that monitors have an incorrect view
of prorgram state.

The AliasedBuffer class provides a future placeholder where this
technique can be used, but writes can still be observed.  To achieve
this, the node-chakra-core fork will add in appropriate tracking logic
in the AliasedBuffer's SetValue() method. Going forward, this class can
evolve to support more sophisticated mechanisms if necessary.

PR-URL: nodejs/node#15077
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ Issues and PRs that require attention from people who are familiar with C++. http2 Issues or PRs related to the http2 subsystem. lib / src Issues and PRs related to general changes in the lib or src directory.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants