-
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 realm binding data store weak #47688
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ | |
namespace node { | ||
namespace shadow_realm { | ||
using v8::Context; | ||
using v8::EscapableHandleScope; | ||
using v8::HandleScope; | ||
using v8::Local; | ||
using v8::MaybeLocal; | ||
|
@@ -15,7 +16,6 @@ using TryCatchScope = node::errors::TryCatchScope; | |
// static | ||
ShadowRealm* ShadowRealm::New(Environment* env) { | ||
ShadowRealm* realm = new ShadowRealm(env); | ||
env->AssignToContext(realm->context(), realm, ContextInfo("")); | ||
|
||
// We do not expect the realm bootstrapping to throw any | ||
// exceptions. If it does, exit the current Node.js instance. | ||
|
@@ -31,39 +31,62 @@ ShadowRealm* ShadowRealm::New(Environment* env) { | |
MaybeLocal<Context> HostCreateShadowRealmContextCallback( | ||
Local<Context> initiator_context) { | ||
Environment* env = Environment::GetCurrent(initiator_context); | ||
EscapableHandleScope scope(env->isolate()); | ||
ShadowRealm* realm = ShadowRealm::New(env); | ||
if (realm != nullptr) { | ||
return realm->context(); | ||
return scope.Escape(realm->context()); | ||
} | ||
return MaybeLocal<Context>(); | ||
} | ||
|
||
// static | ||
void ShadowRealm::WeakCallback(const v8::WeakCallbackInfo<ShadowRealm>& data) { | ||
ShadowRealm* realm = data.GetParameter(); | ||
realm->context_.Reset(); | ||
|
||
// Yield to pending weak callbacks before deleting the realm. | ||
// This is necessary to avoid cleaning up base objects before their scheduled | ||
// weak callbacks are invoked, which can lead to accessing to v8 apis during | ||
// the first pass of the weak callback. | ||
realm->env()->SetImmediate([realm](Environment* env) { delete realm; }); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just remembered that technically shadow realms can outlive the environment...(which is why I added the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is going to lead to crashes if the ream has access to any libuv handles within it. The problem is that any handles could emit data and ultimately call into JS. IMHO the mechanism needs to be more sophisticated and we should track any native resource that the realm can spawn and let the ream be collected only after they have all been disposed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the ideas. Also, I noticed that the ASan build is failing for the URL binding data. I'm going to see how to address the failure too. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've added a cleanup for ShadowRealms when the environment is being freed. This ensures that no shadow realms can outlive the lifetime of the environment. Similar to BaseObjects, no BaseObjects can outlive their creation realm (https://github.com/nodejs/node/blob/main/src/node_realm.cc#L27). As for the ASan failure of the URL binding data, I've removed the aliased buffer's weak callback as the validness of the aliased buffer can be checked with the emptiness of the persistent handle. Would you mind taking a look again? Thank you! |
||
// Remove the cleanup hook to avoid deleting the realm again. | ||
realm->env()->RemoveCleanupHook(DeleteMe, realm); | ||
} | ||
|
||
// static | ||
void ShadowRealm::DeleteMe(void* data) { | ||
ShadowRealm* realm = static_cast<ShadowRealm*>(data); | ||
// Clear the context handle to avoid invoking the weak callback again. | ||
// Also, the context internal slots are cleared and the context is no longer | ||
// reference to the realm. | ||
delete realm; | ||
} | ||
|
||
ShadowRealm::ShadowRealm(Environment* env) | ||
: Realm(env, NewContext(env->isolate()), kShadowRealm) { | ||
env->TrackShadowRealm(this); | ||
context_.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter); | ||
CreateProperties(); | ||
|
||
env->TrackShadowRealm(this); | ||
env->AddCleanupHook(DeleteMe, this); | ||
} | ||
|
||
ShadowRealm::~ShadowRealm() { | ||
while (HasCleanupHooks()) { | ||
RunCleanup(); | ||
} | ||
if (env_ != nullptr) { | ||
env_->UntrackShadowRealm(this); | ||
|
||
env_->UntrackShadowRealm(this); | ||
|
||
if (context_.IsEmpty()) { | ||
// This most likely happened because the weak callback cleared it. | ||
return; | ||
} | ||
} | ||
|
||
void ShadowRealm::OnEnvironmentDestruct() { | ||
CHECK_NOT_NULL(env_); | ||
env_ = nullptr; // This means that the shadow realm has out-lived the | ||
// environment. | ||
{ | ||
HandleScope handle_scope(isolate()); | ||
env_->UnassignFromContext(context()); | ||
} | ||
} | ||
|
||
v8::Local<v8::Context> ShadowRealm::context() const { | ||
|
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.
Can you add a test that creates a bunch of shadow realms and then generates a heap snapshot? I think that's how I ran into the shadow realms outliving shadow realms issue and is why this is necessary.
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.
Added a new test
test/pummel/test-heapdump-shadow-realm.js
.