@@ -37,7 +37,6 @@ using v8::Context;
37
37
using v8::EmbedderGraph;
38
38
using v8::EscapableHandleScope;
39
39
using v8::Function;
40
- using v8::FunctionCallbackInfo;
41
40
using v8::FunctionTemplate;
42
41
using v8::HandleScope;
43
42
using v8::HeapSpaceStatistics;
@@ -58,8 +57,6 @@ using v8::TracingController;
58
57
using v8::TryCatch;
59
58
using v8::Undefined;
60
59
using v8::Value;
61
- using v8::WeakCallbackInfo;
62
- using v8::WeakCallbackType;
63
60
using worker::Worker;
64
61
65
62
int const ContextEmbedderTag::kNodeContextTag = 0x6e6f64 ;
@@ -840,8 +837,6 @@ Environment::~Environment() {
840
837
addon.Close ();
841
838
}
842
839
}
843
-
844
- CHECK_EQ (base_object_count_, 0 );
845
840
}
846
841
847
842
void Environment::InitializeLibuv () {
@@ -1002,11 +997,16 @@ void Environment::RunCleanup() {
1002
997
started_cleanup_ = true ;
1003
998
TRACE_EVENT0 (TRACING_CATEGORY_NODE1 (environment), " RunCleanup" );
1004
999
bindings_.clear ();
1000
+ // Only BaseObject's cleanups are registered as per-realm cleanup hooks now.
1001
+ // Defer the BaseObject cleanup after handles are cleaned up.
1005
1002
CleanupHandles ();
1006
1003
1007
- while (!cleanup_queue_.empty () || native_immediates_.size () > 0 ||
1004
+ while (!cleanup_queue_.empty () || principal_realm_->HasCleanupHooks () ||
1005
+ native_immediates_.size () > 0 ||
1008
1006
native_immediates_threadsafe_.size () > 0 ||
1009
1007
native_immediates_interrupts_.size () > 0 ) {
1008
+ // TODO(legendecas): cleanup handles in per-realm cleanup hooks as well.
1009
+ principal_realm_->RunCleanup ();
1010
1010
cleanup_queue_.Drain ();
1011
1011
CleanupHandles ();
1012
1012
}
@@ -1565,8 +1565,8 @@ void Environment::RemoveUnmanagedFd(int fd) {
1565
1565
1566
1566
void Environment::PrintInfoForSnapshotIfDebug () {
1567
1567
if (enabled_debug_list ()->enabled (DebugCategory::MKSNAPSHOT)) {
1568
- fprintf (stderr, " BaseObjects at the exit of the Environment:\n " );
1569
- PrintAllBaseObjects ();
1568
+ fprintf (stderr, " At the exit of the Environment:\n " );
1569
+ principal_realm ()-> PrintInfoForSnapshot ();
1570
1570
fprintf (stderr, " \n Native modules without cache:\n " );
1571
1571
for (const auto & s : builtins_without_cache) {
1572
1572
fprintf (stderr, " %s\n " , s.c_str ());
@@ -1582,45 +1582,6 @@ void Environment::PrintInfoForSnapshotIfDebug() {
1582
1582
}
1583
1583
}
1584
1584
1585
- void Environment::PrintAllBaseObjects () {
1586
- size_t i = 0 ;
1587
- std::cout << " BaseObjects\n " ;
1588
- ForEachBaseObject ([&](BaseObject* obj) {
1589
- std::cout << " #" << i++ << " " << obj << " : " <<
1590
- obj->MemoryInfoName () << " \n " ;
1591
- });
1592
- }
1593
-
1594
- void Environment::VerifyNoStrongBaseObjects () {
1595
- // When a process exits cleanly, i.e. because the event loop ends up without
1596
- // things to wait for, the Node.js objects that are left on the heap should
1597
- // be:
1598
- //
1599
- // 1. weak, i.e. ready for garbage collection once no longer referenced, or
1600
- // 2. detached, i.e. scheduled for destruction once no longer referenced, or
1601
- // 3. an unrefed libuv handle, i.e. does not keep the event loop alive, or
1602
- // 4. an inactive libuv handle (essentially the same here)
1603
- //
1604
- // There are a few exceptions to this rule, but generally, if there are
1605
- // C++-backed Node.js objects on the heap that do not fall into the above
1606
- // categories, we may be looking at a potential memory leak. Most likely,
1607
- // the cause is a missing MakeWeak() call on the corresponding object.
1608
- //
1609
- // In order to avoid this kind of problem, we check the list of BaseObjects
1610
- // for these criteria. Currently, we only do so when explicitly instructed to
1611
- // or when in debug mode (where --verify-base-objects is always-on).
1612
-
1613
- if (!options ()->verify_base_objects ) return ;
1614
-
1615
- ForEachBaseObject ([](BaseObject* obj) {
1616
- if (obj->IsNotIndicativeOfMemoryLeakAtExit ()) return ;
1617
- fprintf (stderr, " Found bad BaseObject during clean exit: %s\n " ,
1618
- obj->MemoryInfoName ().c_str ());
1619
- fflush (stderr);
1620
- ABORT ();
1621
- });
1622
- }
1623
-
1624
1585
EnvSerializeInfo Environment::Serialize (SnapshotCreator* creator) {
1625
1586
EnvSerializeInfo info;
1626
1587
Local<Context> ctx = context ();
@@ -1639,10 +1600,6 @@ EnvSerializeInfo Environment::Serialize(SnapshotCreator* creator) {
1639
1600
info.should_abort_on_uncaught_toggle =
1640
1601
should_abort_on_uncaught_toggle_.Serialize (ctx, creator);
1641
1602
1642
- // Do this after other creator->AddData() calls so that Snapshotable objects
1643
- // can use 0 to indicate that a SnapshotIndex is invalid.
1644
- SerializeSnapshotableObjects (this , creator, &info);
1645
-
1646
1603
info.principal_realm = principal_realm_->Serialize (creator);
1647
1604
return info;
1648
1605
}
@@ -1716,6 +1673,7 @@ void Environment::BuildEmbedderGraph(Isolate* isolate,
1716
1673
void * data) {
1717
1674
MemoryTracker tracker (isolate, graph);
1718
1675
Environment* env = static_cast <Environment*>(data);
1676
+ // Start traversing embedder objects from the root Environment object.
1719
1677
tracker.Track (env);
1720
1678
}
1721
1679
@@ -1890,153 +1848,4 @@ void Environment::MemoryInfo(MemoryTracker* tracker) const {
1890
1848
void Environment::RunWeakRefCleanup () {
1891
1849
isolate ()->ClearKeptObjects ();
1892
1850
}
1893
-
1894
- // Not really any better place than env.cc at this moment.
1895
- BaseObject::BaseObject (Environment* env, Local<Object> object)
1896
- : persistent_handle_(env->isolate (), object), env_(env) {
1897
- CHECK_EQ (false , object.IsEmpty ());
1898
- CHECK_GE (object->InternalFieldCount (), BaseObject::kInternalFieldCount );
1899
- object->SetAlignedPointerInInternalField (BaseObject::kEmbedderType ,
1900
- &kNodeEmbedderId );
1901
- object->SetAlignedPointerInInternalField (BaseObject::kSlot ,
1902
- static_cast <void *>(this ));
1903
- env->AddCleanupHook (DeleteMe, static_cast <void *>(this ));
1904
- env->modify_base_object_count (1 );
1905
- }
1906
-
1907
- BaseObject::~BaseObject () {
1908
- env ()->modify_base_object_count (-1 );
1909
- env ()->RemoveCleanupHook (DeleteMe, static_cast <void *>(this ));
1910
-
1911
- if (UNLIKELY (has_pointer_data ())) {
1912
- PointerData* metadata = pointer_data ();
1913
- CHECK_EQ (metadata->strong_ptr_count , 0 );
1914
- metadata->self = nullptr ;
1915
- if (metadata->weak_ptr_count == 0 ) delete metadata;
1916
- }
1917
-
1918
- if (persistent_handle_.IsEmpty ()) {
1919
- // This most likely happened because the weak callback below cleared it.
1920
- return ;
1921
- }
1922
-
1923
- {
1924
- HandleScope handle_scope (env ()->isolate ());
1925
- object ()->SetAlignedPointerInInternalField (BaseObject::kSlot , nullptr );
1926
- }
1927
- }
1928
-
1929
- void BaseObject::MakeWeak () {
1930
- if (has_pointer_data ()) {
1931
- pointer_data ()->wants_weak_jsobj = true ;
1932
- if (pointer_data ()->strong_ptr_count > 0 ) return ;
1933
- }
1934
-
1935
- persistent_handle_.SetWeak (
1936
- this ,
1937
- [](const WeakCallbackInfo<BaseObject>& data) {
1938
- BaseObject* obj = data.GetParameter ();
1939
- // Clear the persistent handle so that ~BaseObject() doesn't attempt
1940
- // to mess with internal fields, since the JS object may have
1941
- // transitioned into an invalid state.
1942
- // Refs: https://github.com/nodejs/node/issues/18897
1943
- obj->persistent_handle_ .Reset ();
1944
- CHECK_IMPLIES (obj->has_pointer_data (),
1945
- obj->pointer_data ()->strong_ptr_count == 0 );
1946
- obj->OnGCCollect ();
1947
- },
1948
- WeakCallbackType::kParameter );
1949
- }
1950
-
1951
- // This just has to be different from the Chromium ones:
1952
- // https://source.chromium.org/chromium/chromium/src/+/main:gin/public/gin_embedders.h;l=18-23;drc=5a758a97032f0b656c3c36a3497560762495501a
1953
- // Otherwise, when Node is loaded in an isolate which uses cppgc, cppgc will
1954
- // misinterpret the data stored in the embedder fields and try to garbage
1955
- // collect them.
1956
- uint16_t kNodeEmbedderId = 0x90de ;
1957
-
1958
- void BaseObject::LazilyInitializedJSTemplateConstructor (
1959
- const FunctionCallbackInfo<Value>& args) {
1960
- DCHECK (args.IsConstructCall ());
1961
- CHECK_GE (args.This ()->InternalFieldCount (), BaseObject::kInternalFieldCount );
1962
- args.This ()->SetAlignedPointerInInternalField (BaseObject::kEmbedderType ,
1963
- &kNodeEmbedderId );
1964
- args.This ()->SetAlignedPointerInInternalField (BaseObject::kSlot , nullptr );
1965
- }
1966
-
1967
- Local<FunctionTemplate> BaseObject::MakeLazilyInitializedJSTemplate (
1968
- Environment* env) {
1969
- Local<FunctionTemplate> t = NewFunctionTemplate (
1970
- env->isolate (), LazilyInitializedJSTemplateConstructor);
1971
- t->Inherit (BaseObject::GetConstructorTemplate (env));
1972
- t->InstanceTemplate ()->SetInternalFieldCount (BaseObject::kInternalFieldCount );
1973
- return t;
1974
- }
1975
-
1976
- BaseObject::PointerData* BaseObject::pointer_data () {
1977
- if (!has_pointer_data ()) {
1978
- PointerData* metadata = new PointerData ();
1979
- metadata->wants_weak_jsobj = persistent_handle_.IsWeak ();
1980
- metadata->self = this ;
1981
- pointer_data_ = metadata;
1982
- }
1983
- CHECK (has_pointer_data ());
1984
- return pointer_data_;
1985
- }
1986
-
1987
- void BaseObject::decrease_refcount () {
1988
- CHECK (has_pointer_data ());
1989
- PointerData* metadata = pointer_data ();
1990
- CHECK_GT (metadata->strong_ptr_count , 0 );
1991
- unsigned int new_refcount = --metadata->strong_ptr_count ;
1992
- if (new_refcount == 0 ) {
1993
- if (metadata->is_detached ) {
1994
- OnGCCollect ();
1995
- } else if (metadata->wants_weak_jsobj && !persistent_handle_.IsEmpty ()) {
1996
- MakeWeak ();
1997
- }
1998
- }
1999
- }
2000
-
2001
- void BaseObject::increase_refcount () {
2002
- unsigned int prev_refcount = pointer_data ()->strong_ptr_count ++;
2003
- if (prev_refcount == 0 && !persistent_handle_.IsEmpty ())
2004
- persistent_handle_.ClearWeak ();
2005
- }
2006
-
2007
- void BaseObject::DeleteMe (void * data) {
2008
- BaseObject* self = static_cast <BaseObject*>(data);
2009
- if (self->has_pointer_data () &&
2010
- self->pointer_data ()->strong_ptr_count > 0 ) {
2011
- return self->Detach ();
2012
- }
2013
- delete self;
2014
- }
2015
-
2016
- bool BaseObject::IsDoneInitializing () const { return true ; }
2017
-
2018
- Local<Object> BaseObject::WrappedObject () const {
2019
- return object ();
2020
- }
2021
-
2022
- bool BaseObject::IsRootNode () const {
2023
- return !persistent_handle_.IsWeak ();
2024
- }
2025
-
2026
- Local<FunctionTemplate> BaseObject::GetConstructorTemplate (
2027
- IsolateData* isolate_data) {
2028
- Local<FunctionTemplate> tmpl = isolate_data->base_object_ctor_template ();
2029
- if (tmpl.IsEmpty ()) {
2030
- tmpl = NewFunctionTemplate (isolate_data->isolate (), nullptr );
2031
- tmpl->SetClassName (
2032
- FIXED_ONE_BYTE_STRING (isolate_data->isolate (), " BaseObject" ));
2033
- isolate_data->set_base_object_ctor_template (tmpl);
2034
- }
2035
- return tmpl;
2036
- }
2037
-
2038
- bool BaseObject::IsNotIndicativeOfMemoryLeakAtExit () const {
2039
- return IsWeakOrDetached ();
2040
- }
2041
-
2042
1851
} // namespace node
0 commit comments