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

src: add HandleScope to fix error #19972

Closed
wants to merge 1 commit into from

Conversation

ryzokuken
Copy link
Contributor

@ryzokuken ryzokuken commented Apr 12, 2018

Add HandleErrors to the AsyncScope constructor and destructor in
async_wrap-inl.h to fix "FATAL ERROR" incurring observed while running
test-http2-respond-with-file using the --trace-events-enabled flag.

Fixes: #19921

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • commit message follows commit guidelines

cc @addaleax @jasnell

Add `HandleError`s to the AsyncScope constructor and destructor in
async_wrap-inl.h to fix "FATAL ERROR" incurring observed while running
test-http2-respond-with-file using the --trace-events-enabled flag.

Fixes: nodejs#19921
@nodejs-github-bot nodejs-github-bot added the c++ Issues and PRs that require attention from people who are familiar with C++. label Apr 12, 2018
@ryzokuken
Copy link
Contributor Author

ryzokuken commented Apr 12, 2018

I'm also testing this locally. Meanwhile, could you help me understand what exactly was wrong and how it was fixed by adding the two HandleScopes? That'd help me get started towards contributing more towards this part of the project.

@ryzokuken
Copy link
Contributor Author

The error says: FATAL ERROR: v8::HandleScope::CreateHandle() Cannot create a handle without a HandleScope, so it makes sense that making a HandleScope would solve a thing or two, but where was HandleScope::CreateHandle() called in the first place?

@addaleax
Copy link
Member

Meanwhile, could you help me understand what exactly was wrong and how it was fixed by adding the two HandleScopes?

@ryzokuken When accessing JS values from C++, what the engine provides in order to to that is a handle object, i.e. Local<Value> or some more specific kind of Local<>, that refers to the JS value.

As an optimization, these handles do not directly manage the lifetime of JS objects, like one might expect from C++ objects. Instead, V8 uses the concept of a handle scope: These form a stack, and new handles are always added to the scope on top of the stack. That scope is basically a GC root – all JS objects that are registered with it are kept alive, until the scope is closed.

In Node.js, if our code is called from e.g. libuv, we need such a scope that is inside of the callback. Otherwise, any JS object we’re creating or getting a reference to in some other way, would be registered in a scope that is outside of the event loop. That would mean that these objects cannot be garbage-collected until the event loop finishes, creating a memory leak.

(Having an error instead of a memory leak was implemented because it made checking for these kinds of leaks inside Node.js easier, btw.)

but where was HandleScope::CreateHandle() called in the first place?

According to the stack trace from #19921, that would be the Number::New call inside Emit() in async-wrap.cc.

It might make more sense to add the handle scope there, because that’s where they are being used. However, while HandleScopes are already pretty light-weight, that’s a hot path and we’d need to benchmark such a change.

@BridgeAR
Copy link
Member

@BridgeAR BridgeAR added the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Apr 13, 2018
jasnell pushed a commit that referenced this pull request Apr 14, 2018
Add `HandleError`s to the AsyncScope constructor and destructor in
async_wrap-inl.h to fix "FATAL ERROR" incurring observed while running
test-http2-respond-with-file using the --trace-events-enabled flag.

Fixes: #19921

PR-URL: #19972
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
@jasnell
Copy link
Member

jasnell commented Apr 14, 2018

Landed in 6eaeabc

@jasnell jasnell closed this Apr 14, 2018
@hashseed
Copy link
Member

Guess I saw this too late. Current solution works. But I would just put a v8::HandleScope in Emit.

@ryzokuken
Copy link
Contributor Author

@hashseed yes, that sounds great too, but I wonder: do we need v8::HandleScope for all calls to Emit? If not, then perhaps it makes more sense to manually call if like we've done?

@hashseed
Copy link
Member

hashseed commented Apr 14, 2018

Yes. Emit allocates via Number::New. If the caller has not created a handle scope, this will crash. Creating a handle scope is fairly cheap, so it simply makes sense that the function that performs the allocation also provides the handle scope, and does not defer to the caller to fulfill this implicit contract.

@ryzokuken
Copy link
Contributor Author

@hashseed that makes a lot of sense. Forgive me for the noobish question, but what if v8::HandleScope is called twice?

@addaleax
Copy link
Member

@ryzokuken You end up with two nested HandleScopes, and all handles inside the inner one will be registered only with that one and will be made invalid once that scope is left, even though there is still the outer one.

If you create two handle scopes immediately after one another, the effect will be the same as if there was only one – there just won’t be any handles associated with the outer one.

@hashseed
Copy link
Member

What Anna said. Having a nested handle scope is fine.

@ryzokuken
Copy link
Contributor Author

@addaleax @hashseed In that case, calling v8::HandleScope right inside Emit won't break anything. We could do that and then remove any redundant calls to v8::HandleScope, but nothing will break even if we somehow miss some. On the other hand, this would ensure that there won't be any accidental crashes in the future just because someone missed a v8::HandleScope call before calling Emit (exactly what happened here, right?)

So, what do you say? Should I also add a call inside Emit, then set out to remove redundant calls?

@addaleax
Copy link
Member

I mean, if @hashseed says it’s fine from a performance point of view, I believe that. :) You can feel free to make that switch.

@hashseed
Copy link
Member

Sounds like a plan :)

@ryzokuken
Copy link
Contributor Author

ryzokuken commented Apr 14, 2018

@addaleax @hashseed Great! Will make a PR first thing in the morning tomorrow (it's late here now).

jasnell pushed a commit that referenced this pull request Apr 16, 2018
Add `HandleError`s to the AsyncScope constructor and destructor in
async_wrap-inl.h to fix "FATAL ERROR" incurring observed while running
test-http2-respond-with-file using the --trace-events-enabled flag.

Fixes: #19921

PR-URL: #19972
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
ryzokuken added a commit to ryzokuken/node that referenced this pull request Apr 17, 2018
Remove redundant calls to v8::HandleScope in the contructor and
destructor for the AsyncScope class

Refs: nodejs#19972 (comment)
Refs: nodejs#20045
ryzokuken added a commit to ryzokuken/node that referenced this pull request Apr 19, 2018
Move v8::HandleScope call to Emit removing it from previous locations
where it was added to avoid crashing (constructor and destructor of
AsyncWrap) for a more general and fool-proof solution.

Ref: nodejs#19972 (comment)
ryzokuken added a commit that referenced this pull request Apr 20, 2018
Move v8::HandleScope call to Emit removing it from previous locations
where it was added to avoid crashing (constructor and destructor of
AsyncWrap) for a more general and fool-proof solution.

Ref: #19972 (comment)

PR-URL: #20045
Reviewed-By: Yang Guo <yangguo@chromium.org>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com>
ryzokuken added a commit to ryzokuken/node that referenced this pull request Apr 20, 2018
jasnell pushed a commit that referenced this pull request Apr 20, 2018
Move v8::HandleScope call to Emit removing it from previous locations
where it was added to avoid crashing (constructor and destructor of
AsyncWrap) for a more general and fool-proof solution.

Ref: #19972 (comment)

PR-URL: #20045
Reviewed-By: Yang Guo <yangguo@chromium.org>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
author ready PRs that have at least one approval, no pending requests for changes, and a CI started. c++ Issues and PRs that require attention from people who are familiar with C++.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Abort when using --trace-events-enabled on test-http2-respond-with-file
6 participants