-
Notifications
You must be signed in to change notification settings - Fork 29.8k
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: make AtExit callback's per Environment #9163
Conversation
src/node.cc
Outdated
} | ||
|
||
|
||
thread_local Environment* thread_local_env; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Should this be
static
? - I think this doesn’t need an explicit initializer, but would you mind adding
= nullptr
just for clarity?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd leave out the = nullptr
, it looks unidiomatic. It should be static though, yes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@addaleax @bnoordhuis I'd like to make sure I understand the reason for making this static
. My understanding is that a thread_local
object is allocated when the thread begins and deallocated when the thread ends, so static
in this case does not refer to storage but to linkage. And without the explicitstatic
, the linkage would be external
by default. Does that sound correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct. static
in this context is about linkage, not lifetime. Without static
, the variable is visible to other compilation units.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bnoordhuis Thanks for clarifying that, I appreciate it.
src/env.h
Outdated
AtExitCallback* next_; | ||
void (*cb_)(void* arg); | ||
void* arg_; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a suggestion, but you could make AtExitCallback
a forward declaration in the node
namespace here and only define it right before AtExit
/RunAtExit
. That would make sense insofar as the exact layout isn’t part of the API here, and having the definition of the struct
close to its actual usage might be a tad more readable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sounds good, I'll change that.
src/node.cc
Outdated
delete p; | ||
p = q; | ||
} | ||
env->RunAtExit(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CHECK_NE(env, nullptr);
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A CHECK won't hurt but OTOH, if it's a nullptr, the caller is going to find out soon enough. :-)
src/env.h
Outdated
@@ -503,6 +503,9 @@ class Environment { | |||
|
|||
inline v8::Local<v8::Object> NewInternalFieldObject(); | |||
|
|||
inline void AtExit(void (*cb)(void* arg), void* arg); | |||
inline void RunAtExit(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RunAtExit
was a confusing name when I first read it… I don’t have anything better in mind rn, but if you do, feel free to add that here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about RunAtExitCallbacks
?
src/node.h
Outdated
@@ -209,7 +209,8 @@ NODE_EXTERN void FreeEnvironment(Environment* env); | |||
|
|||
NODE_EXTERN void EmitBeforeExit(Environment* env); | |||
NODE_EXTERN int EmitExit(Environment* env); | |||
NODE_EXTERN void RunAtExit(Environment* env); | |||
NODE_DEPRECATED("Use Environment::RunAtExit()", | |||
NODE_EXTERN void RunAtExit(Environment* env)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, one more thing: env.h
is not a public header, so if anyone actually wants to use RunAtExit
in an addon, deprecating this puts them in quite the difficult position. ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh that does not sound good, I'll revert that :)
OS X CI failure: Maybe @bnoordhuis has a bit more context about who actually uses multiple |
src/env-inl.h
Outdated
p->arg_ = arg; | ||
p->next_ = at_exit_functions_; | ||
at_exit_functions_ = p; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These don't really need to be inlined. I'd move it to src/env.cc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great, I'll move them.
src/node.cc
Outdated
delete p; | ||
p = q; | ||
} | ||
env->RunAtExit(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A CHECK won't hurt but OTOH, if it's a nullptr, the caller is going to find out soon enough. :-)
src/node.cc
Outdated
} | ||
|
||
|
||
thread_local Environment* thread_local_env; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd leave out the = nullptr
, it looks unidiomatic. It should be static though, yes.
Yeah, I was kind of expecting that. If we had still supported VS 2013, that would have been a problem, too. @danbev You can use
Embedders like electron and plask do. Electron for example has (or had) a context per tab. |
Forgot to mention, the OS X issue could be fixed by linking to libc++ instead of libstdc++ but that has some wider ranging implications and would make the pull request unsuitable for back-porting to v4.x and v6.x. EDIT: Scratch that, we already seem to be doing that in master? I see a |
c133999
to
83c7a88
Compare
I was not aware of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at this again, it doesn't seem quite correct. The thread_local is set once but of course you can more than one Environment
per isolate.
It's currently mostly academic because StartNodeInstance()
is only called from Start()
but still.
src/env-inl.h
Outdated
@@ -443,6 +443,7 @@ inline v8::Local<v8::Object> Environment::NewInternalFieldObject() { | |||
return m_obj.ToLocalChecked(); | |||
} | |||
|
|||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unnecessary whitespace change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll remove this.
src/env.h
Outdated
@@ -578,6 +581,9 @@ class Environment { | |||
|
|||
char* http_parser_buffer_; | |||
|
|||
struct AtExitCallback; | |||
AtExitCallback* at_exit_functions_ = nullptr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the record, I'd be okay with switching this a std::list<...>
. Could be a separate commit or pull request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good, let me do that in a separate PR.
I'll take another look into this |
27de753
to
a7c59da
Compare
Rebased with upstream master. Running CI again to see how much damage I've cause when updating node.gyp: This does not address @bnoordhuis point that Start can be called multiple times. I'll take a look at this next. |
c840ef1
to
efac3b7
Compare
Sorry about the delay on this. I've tried to address the issue of having multiple Environments per Isolate for the use case when Node is being embedded (see 45248a4d5f1794174a44a56186c5d9266ff75087 for details). I'm trying to understand the other case when $ make cctest GTEST_FILTER=NodeTest.*
Note: Google Test filter = NodeTest.*
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from NodeTest
[ RUN ] NodeTest.StartMultipleTimes
#
# Fatal error in ../deps/v8/src/v8.cc, line 94
# Check failed: !platform_.
#
==== C stack trace ===============================
0 cctest 0x0000000100325b4e v8::base::debug::StackTrace::StackTrace() + 30
1 cctest 0x0000000100325b85 v8::base::debug::StackTrace::StackTrace() + 21
2 cctest 0x000000010031e914 V8_Fatal + 452
3 cctest 0x00000001010c996d v8::internal::V8::InitializePlatform(v8::Platform*) + 77
4 cctest 0x000000010035e8e5 v8::V8::InitializePlatform(v8::Platform*) + 21
5 cctest 0x0000000100033b20 _ZN4node3$_010InitializeEi + 48
6 cctest 0x000000010003371e node::Start(int, char**) + 158
7 cctest 0x00000001000cee4f startNode(void*) + 31
8 libsystem_pthread.dylib 0x00007fff8423a99d _pthread_body + 131
9 libsystem_pthread.dylib 0x00007fff8423a91a _pthread_body + 0
10 libsystem_pthread.dylib 0x00007fff84238351 thread_start + 13
make: *** [cctest] Illegal instruction: 4 I know @bnoordhuis said that it was mostly academic, and I'm wondering if this is worth pursuing? Also, adding a cctest that used Node core bits was interesting and took a while to get it to pass on all platforms. Would it make sense to clean up test: enable gtest that use node core code and add this separately or is there any policy to avoid cctest in favour of other tests? |
7859e8a
to
0c2e578
Compare
Can you squash into logical commits? I understand you made some additional changes but the history is kind of hard to follow. |
0c2e578
to
e63668d
Compare
@bnoordhuis Sorry about that, most of them came about as I was trying to figure out how to get the test passing on the various platforms (after adding a test to cctest). |
This commit tries to make it simpler to add unit tests (cctest) for code that needs to test node core funtionality but that might not be appropriate as an addon or a JavaScript test. An example of this could be adding functionality targeted for situations when Node itself is embedded. Currently it was not as easy, or efficient, as one would have hoped to add such tests. The object output directories vary for different operating systems which we need to link to so that we don't have an additional compilation step. PR-URL: nodejs#11956 Backport-PR-URL: nodejs#12948 Ref: nodejs#9163 Reviewed-By: James M Snell <[email protected]>
This commit tries to make it simpler to add unit tests (cctest) for code that needs to test node core funtionality but that might not be appropriate as an addon or a JavaScript test. An example of this could be adding functionality targeted for situations when Node itself is embedded. Currently it was not as easy, or efficient, as one would have hoped to add such tests. The object output directories vary for different operating systems which we need to link to so that we don't have an additional compilation step. PR-URL: #11956 Backport-PR-URL: #12948 Ref: #9163 Reviewed-By: James M Snell <[email protected]>
this is a re-base of the gyp part of 6a09a69 after bumping GYP version to https://chromium.googlesource.com/external/gyp/+/eb296f67da078ec01f5e3a9ea9cdc6d26d680161 Original-PR-URL: nodejs#11956 Original-Ref: nodejs#9163 Original-Reviewed-By: James M Snell <[email protected]> PR-URL: nodejs#12450 Reviewed-By: João Reis <[email protected]>
This change was suggested by bnoordhuis in the following comment: nodejs/node#9163 (comment) Not including any tests as this is covered by test/addons/at-exit. PR-URL: nodejs/node#12255 Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: Richard Lau <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Anna Henningsen <[email protected]>
this is a re-base of the gyp part of 6a09a69 after bumping GYP version to https://chromium.googlesource.com/external/gyp/+/eb296f67da078ec01f5e3a9ea9cdc6d26d680161 Original-PR-URL: nodejs#11956 Original-Ref: nodejs#9163 Original-Reviewed-By: James M Snell <[email protected]> PR-URL: nodejs#12450 Reviewed-By: João Reis <[email protected]>
this is a re-base of the gyp part of 6a09a69 after bumping GYP version to https://chromium.googlesource.com/external/gyp/+/eb296f67da078ec01f5e3a9ea9cdc6d26d680161 Original-PR-URL: nodejs/node#11956 Original-Ref: nodejs/node#9163 Original-Reviewed-By: James M Snell <[email protected]> PR-URL: nodejs/node#12450 Reviewed-By: João Reis <[email protected]>
this is a re-base of the gyp part of 6a09a69 after bumping GYP version to https://chromium.googlesource.com/external/gyp/+/eb296f67da078ec01f5e3a9ea9cdc6d26d680161 Original-PR-URL: nodejs/node#11956 Original-Ref: nodejs/node#9163 Original-Reviewed-By: James M Snell <[email protected]> PR-URL: nodejs/node#12450 Reviewed-By: João Reis <[email protected]>
this is a re-base of the gyp part of 6a09a69 after bumping GYP version to https://chromium.googlesource.com/external/gyp/+/eb296f67da078ec01f5e3a9ea9cdc6d26d680161 Original-PR-URL: #11956 Original-Ref: #9163 Original-Reviewed-By: James M Snell <[email protected]> PR-URL: #12450 Reviewed-By: João Reis <[email protected]>
this is a re-base of the gyp part of 6a09a69 after bumping GYP version to https://chromium.googlesource.com/external/gyp/+/eb296f67da078ec01f5e3a9ea9cdc6d26d680161 Original-PR-URL: #11956 Original-Ref: #9163 Original-Reviewed-By: James M Snell <[email protected]> PR-URL: #12450 Reviewed-By: João Reis <[email protected]>
For right now @nodejs/lts decided to not land this as we didn't see a specific reason to backport. If this may be mistaken please let us know |
Looks like this causes an issue with electron, see electron/electron#11299 (comment). I don't know enough about this PR to be able to comment on whether the solution proposed in that thread is a good idea or not, but maybe @danbev @addaleax or @bnoordhuis might be able to offer some advice there. |
@gibfahn I'll take a closer look at this (hopefully later today or tomorrow). |
I've started to look into this, but won't be working for the next few (sick kid), but I'll revisit as soon as I'm back. |
This commit set the Environment as a thread local when CreateEnvironment is called which is currently not being done. This would lead to a segment fault if later node::AtExit is called without specifying the environment parameter. This specific issue was reported by Electron. If I recall correctly, back when this was implemented the motivation was that if embedders have multiple environments per isolate they should be using the AtExit functions that take an environment. This is not the case with Electron which only create a single environment (as far as I know), and if a native module calls AtExit this would lead to the segment fault. I was able to reproduce Electron issue and the provided test simulates it. I was also able to use this patch and verify that it works for the Electron issue as well. Refs: nodejs#9163 Refs: electron/electron#11299
This commit set the Environment as a thread local when CreateEnvironment is called which is currently not being done. This would lead to a segment fault if later node::AtExit is called without specifying the environment parameter. This specific issue was reported by Electron. If I recall correctly, back when this was implemented the motivation was that if embedders have multiple environments per isolate they should be using the AtExit functions that take an environment. This is not the case with Electron which only create a single environment (as far as I know), and if a native module calls AtExit this would lead to the segment fault. I was able to reproduce Electron issue and the provided test simulates it. I was also able to use this patch and verify that it works for the Electron issue as well. PR-URL: nodejs#18573 Refs: nodejs#9163 Refs: electron/electron#11299 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: Matheus Marchini <[email protected]>
This commit set the Environment as a thread local when CreateEnvironment is called which is currently not being done. This would lead to a segment fault if later node::AtExit is called without specifying the environment parameter. This specific issue was reported by Electron. If I recall correctly, back when this was implemented the motivation was that if embedders have multiple environments per isolate they should be using the AtExit functions that take an environment. This is not the case with Electron which only create a single environment (as far as I know), and if a native module calls AtExit this would lead to the segment fault. I was able to reproduce Electron issue and the provided test simulates it. I was also able to use this patch and verify that it works for the Electron issue as well. PR-URL: nodejs#18573 Refs: nodejs#9163 Refs: electron/electron#11299 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: Matheus Marchini <[email protected]>
This commit set the Environment as a thread local when CreateEnvironment is called which is currently not being done. This would lead to a segment fault if later node::AtExit is called without specifying the environment parameter. This specific issue was reported by Electron. If I recall correctly, back when this was implemented the motivation was that if embedders have multiple environments per isolate they should be using the AtExit functions that take an environment. This is not the case with Electron which only create a single environment (as far as I know), and if a native module calls AtExit this would lead to the segment fault. I was able to reproduce Electron issue and the provided test simulates it. I was also able to use this patch and verify that it works for the Electron issue as well. PR-URL: nodejs#18573 Refs: nodejs#9163 Refs: electron/electron#11299 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: Matheus Marchini <[email protected]>
this is a re-base of the gyp part of 6a09a69ec9d36b705e9bde2ac1a193566a702d96 after bumping GYP version to https://chromium.googlesource.com/external/gyp/+/eb296f67da078ec01f5e3a9ea9cdc6d26d680161 Original-PR-URL: nodejs/node#11956 Original-Ref: nodejs/node#9163 Original-Reviewed-By: James M Snell <[email protected]> PR-URL: nodejs/node#12450 Reviewed-By: João Reis <[email protected]>
this is a re-base of the gyp part of 6a09a69ec9d36b705e9bde2ac1a193566a702d96 after bumping GYP version to https://chromium.googlesource.com/external/gyp/+/eb296f67da078ec01f5e3a9ea9cdc6d26d680161 Original-PR-URL: nodejs/node#11956 Original-Ref: nodejs/node#9163 Original-Reviewed-By: James M Snell <[email protected]> PR-URL: nodejs/node#12450 Reviewed-By: João Reis <[email protected]>
Checklist
make -j8 test
(UNIX), orvcbuild test nosign
(Windows) passesAffected core subsystem(s)
src
Description of change
This commit attempts to address one of the TODOs in
#4641 regarding making the AtExit
callback's per environment, instead of the current global.
bnoordhuis provided a few options for solving this and one was to use
a thread-local which is what this commit attempts to do..