imported patch abstract-holder-map draft
authorJon Coppeard <jcoppeard@mozilla.com>
Fri, 16 Aug 2019 13:14:07 +0100
changeset 2220752 e97485c4751534d6b4555cfd4ea933c1630cea5c
parent 2220490 1e2f3d02975d11e67464efe6a034c2a737ecd654
child 2220753 f84fca9a987aeea005b6e719bdae2cc0eded0e20
child 2220783 c56ac0368e07c89e0f62345349cf1503f5e351a8
push id407002
push userjcoppeard@mozilla.com
push dateFri, 16 Aug 2019 13:32:05 +0000
treeherdertry@5b7007ce4796 [default view] [failures only]
milestone70.0a1
imported patch abstract-holder-map
xpcom/base/CycleCollectedJSRuntime.cpp
xpcom/base/CycleCollectedJSRuntime.h
--- a/xpcom/base/CycleCollectedJSRuntime.cpp
+++ b/xpcom/base/CycleCollectedJSRuntime.cpp
@@ -467,24 +467,92 @@ static const JSZoneParticipant sJSZoneCy
 static void JSObjectsTenuredCb(JSContext* aContext, void* aData) {
   static_cast<CycleCollectedJSRuntime*>(aData)->JSObjectsTenured();
 }
 
 static void MozCrashWarningReporter(JSContext*, JSErrorReport*) {
   MOZ_CRASH("Why is someone touching JSAPI without an AutoJSAPI?");
 }
 
+JSHolderMap::JSHolderMap()
+    : mJSHolderMap(256) {}
+
+bool JSHolderMap::Has(void* aHolder) const {
+  return Get(aHolder);
+}
+
+nsScriptObjectTracer* JSHolderMap::Get(void* aHolder) const {
+  Entry* info = nullptr;
+  if (!mJSHolderMap.Get(aHolder, &info)) {
+    return nullptr;
+  }
+
+  MOZ_ASSERT(info->mHolder == aHolder);
+  return info->mTracer;
+}
+
+nsScriptObjectTracer* JSHolderMap::GetAndRemove(void* aHolder) {
+  auto entry = mJSHolderMap.Lookup(aHolder);
+  if (!entry) {
+    return nullptr;
+  }
+
+  Entry* info = entry.Data();
+  MOZ_ASSERT(info->mHolder == aHolder);
+  nsScriptObjectTracer* tracer = info->mTracer;
+
+  Entry* lastInfo = &mJSHolders.GetLast();
+  bool updateLast = (info != lastInfo);
+  if (updateLast) {
+    *info = *lastInfo;
+  }
+
+  mJSHolders.PopLast();
+  entry.Remove();
+
+  if (updateLast) {
+    // We have to do this after removing the entry above to ensure that we don't
+    // trip over the hashtable generation number assertion.
+    mJSHolderMap.Put(info->mHolder, info);
+  }
+
+  return tracer;
+}
+
+void JSHolderMap::Put(void* aHolder, nsScriptObjectTracer* aTracer) {
+  auto entry = mJSHolderMap.LookupForAdd(aHolder);
+  if (entry) {
+    Entry* info = entry.Data();
+    MOZ_ASSERT(info->mHolder == aHolder);
+    info->mTracer = aTracer;
+    return;
+  }
+
+  mJSHolders.InfallibleAppend(Entry{aHolder, aTracer});
+  entry.OrInsert([&] { return &mJSHolders.GetLast(); });
+}
+
+size_t JSHolderMap::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
+  size_t n = 0;
+
+  // We're deliberately not measuring anything hanging off the entries in
+  // mJSHolders.
+  n += mJSHolders.SizeOfExcludingThis(aMallocSizeOf);
+  n += mJSHolderMap.ShallowSizeOfExcludingThis(aMallocSizeOf);
+
+  return n;
+}
+
 CycleCollectedJSRuntime::CycleCollectedJSRuntime(JSContext* aCx)
     : mGCThingCycleCollectorGlobal(sGCThingCycleCollectorGlobal),
       mJSZoneCycleCollectorGlobal(sJSZoneCycleCollectorGlobal),
       mJSRuntime(JS_GetRuntime(aCx)),
       mHasPendingIdleGCTask(false),
       mPrevGCSliceCallback(nullptr),
       mPrevGCNurseryCollectionCallback(nullptr),
-      mJSHolderMap(256),
       mOutOfMemoryState(OOMState::OK),
       mLargeAllocationFailureState(OOMState::OK)
 #ifdef DEBUG
       ,
       mShutdownCalled(false)
 #endif
 {
   MOZ_COUNT_CTOR(CycleCollectedJSRuntime);
@@ -580,24 +648,17 @@ void CycleCollectedJSRuntime::AddContext
 }
 
 void CycleCollectedJSRuntime::RemoveContext(CycleCollectedJSContext* aContext) {
   aContext->removeFrom(mContexts);
 }
 
 size_t CycleCollectedJSRuntime::SizeOfExcludingThis(
     MallocSizeOf aMallocSizeOf) const {
-  size_t n = 0;
-
-  // We're deliberately not measuring anything hanging off the entries in
-  // mJSHolders.
-  n += mJSHolders.SizeOfExcludingThis(aMallocSizeOf);
-  n += mJSHolderMap.ShallowSizeOfExcludingThis(aMallocSizeOf);
-
-  return n;
+  return mJSHolders.SizeOfExcludingThis(aMallocSizeOf);
 }
 
 void CycleCollectedJSRuntime::UnmarkSkippableJSHolders() {
   // Prevent nsWrapperCaches accessed under CanSkip from adding recorded events
   // which might not replay in the same order.
   recordreplay::AutoDisallowThreadEvents disallow;
 
   for (auto iter = mJSHolders.Iter(); !iter.Done(); iter.Next()) {
@@ -1023,26 +1084,17 @@ void CycleCollectedJSRuntime::TraceNativ
     void* holder = iter.Get().mHolder;
     nsScriptObjectTracer* tracer = iter.Get().mTracer;
     tracer->Trace(holder, JsGcTracer(), aTracer);
   }
 }
 
 void CycleCollectedJSRuntime::AddJSHolder(void* aHolder,
                                           nsScriptObjectTracer* aTracer) {
-  auto entry = mJSHolderMap.LookupForAdd(aHolder);
-  if (entry) {
-    JSHolderInfo* info = entry.Data();
-    MOZ_ASSERT(info->mHolder == aHolder);
-    info->mTracer = aTracer;
-    return;
-  }
-
-  mJSHolders.InfallibleAppend(JSHolderInfo{aHolder, aTracer});
-  entry.OrInsert([&] { return &mJSHolders.GetLast(); });
+  mJSHolders.Put(aHolder, aTracer);
 }
 
 struct ClearJSHolder : public TraceCallbacks {
   virtual void Trace(JS::Heap<JS::Value>* aPtr, const char*,
                      void*) const override {
     aPtr->setUndefined();
   }
 
@@ -1077,58 +1129,38 @@ struct ClearJSHolder : public TraceCallb
 
   virtual void Trace(JS::Heap<JSFunction*>* aPtr, const char*,
                      void*) const override {
     *aPtr = nullptr;
   }
 };
 
 void CycleCollectedJSRuntime::RemoveJSHolder(void* aHolder) {
-  auto entry = mJSHolderMap.Lookup(aHolder);
-  if (entry) {
-    JSHolderInfo* info = entry.Data();
-    MOZ_ASSERT(info->mHolder == aHolder);
-    info->mTracer->Trace(aHolder, ClearJSHolder(), nullptr);
-
-    JSHolderInfo* lastInfo = &mJSHolders.GetLast();
-    bool updateLast = (info != lastInfo);
-    if (updateLast) {
-      *info = *lastInfo;
-    }
-
-    mJSHolders.PopLast();
-    entry.Remove();
-
-    if (updateLast) {
-      // We have to do this after removing the entry above to ensure that we
-      // don't trip over the hashtable generation number assertion.
-      mJSHolderMap.Put(info->mHolder, info);
-    }
+  nsScriptObjectTracer* tracer = mJSHolders.GetAndRemove(aHolder);
+  if (tracer) {
+    tracer->Trace(aHolder, ClearJSHolder(), nullptr);
   }
 }
 
 #ifdef DEBUG
 bool CycleCollectedJSRuntime::IsJSHolder(void* aHolder) {
-  return mJSHolderMap.Get(aHolder, nullptr);
+  return mJSHolders.Has(aHolder);
 }
 
 static void AssertNoGcThing(JS::GCCellPtr aGCThing, const char* aName,
                             void* aClosure) {
   MOZ_ASSERT(!aGCThing);
 }
 
 void CycleCollectedJSRuntime::AssertNoObjectsToTrace(void* aPossibleJSHolder) {
-  JSHolderInfo* info = nullptr;
-  if (!mJSHolderMap.Get(aPossibleJSHolder, &info)) {
-    return;
+  nsScriptObjectTracer* tracer = mJSHolders.Get(aPossibleJSHolder);
+  if (tracer) {
+    tracer->Trace(aPossibleJSHolder, TraceCallbackFunc(AssertNoGcThing),
+                  nullptr);
   }
-
-  MOZ_ASSERT(info->mHolder == aPossibleJSHolder);
-  info->mTracer->Trace(aPossibleJSHolder, TraceCallbackFunc(AssertNoGcThing),
-                       nullptr);
 }
 #endif
 
 nsCycleCollectionParticipant* CycleCollectedJSRuntime::GCThingParticipant() {
   return &mGCThingCycleCollectorGlobal;
 }
 
 nsCycleCollectionParticipant* CycleCollectedJSRuntime::ZoneParticipant() {
--- a/xpcom/base/CycleCollectedJSRuntime.h
+++ b/xpcom/base/CycleCollectedJSRuntime.h
@@ -80,19 +80,41 @@ class JSZoneParticipant : public nsCycle
   NS_IMETHOD TraverseNative(void* aPtr,
                             nsCycleCollectionTraversalCallback& aCb) override;
 
   NS_DECL_CYCLE_COLLECTION_CLASS_NAME_METHOD(JSZoneParticipant)
 };
 
 class IncrementalFinalizeRunnable;
 
-struct JSHolderInfo {
-  void* mHolder;
-  nsScriptObjectTracer* mTracer;
+// A map from JS holders to tracer objects, where the values are stored in a
+// SegmentedVector to speed up iteration.
+class JSHolderMap {
+ public:
+  struct Entry {
+    void* mHolder;
+    nsScriptObjectTracer* mTracer;
+  };
+
+  using EntryVector = SegmentedVector<Entry, 1024, InfallibleAllocPolicy>;
+
+  JSHolderMap();
+
+  const EntryVector::IterImpl Iter() { return mJSHolders.Iter(); }
+
+  bool Has(void* aHolder) const;
+  nsScriptObjectTracer* Get(void* aHolder) const;
+  nsScriptObjectTracer* GetAndRemove(void* aHolder);
+  void Put(void* aHolder, nsScriptObjectTracer* aTracer);
+
+  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
+
+ private:
+  EntryVector mJSHolders;
+  nsDataHashtable<nsPtrHashKey<void>, Entry*> mJSHolderMap;
 };
 
 class CycleCollectedJSRuntime {
   friend class JSGCThingParticipant;
   friend class JSZoneParticipant;
   friend class IncrementalFinalizeRunnable;
   friend class CycleCollectedJSContext;
 
@@ -310,18 +332,17 @@ class CycleCollectedJSRuntime {
   JSRuntime* mJSRuntime;
   bool mHasPendingIdleGCTask;
 
   JS::GCSliceCallback mPrevGCSliceCallback;
   JS::GCNurseryCollectionCallback mPrevGCNurseryCollectionCallback;
 
   mozilla::TimeStamp mLatestNurseryCollectionStart;
 
-  SegmentedVector<JSHolderInfo, 1024, InfallibleAllocPolicy> mJSHolders;
-  nsDataHashtable<nsPtrHashKey<void>, JSHolderInfo*> mJSHolderMap;
+  JSHolderMap mJSHolders;
 
   typedef nsDataHashtable<nsFuncPtrHashKey<DeferredFinalizeFunction>, void*>
       DeferredFinalizerTable;
   DeferredFinalizerTable mDeferredFinalizerTable;
 
   RefPtr<IncrementalFinalizeRunnable> mFinalizeRunnable;
 
   OOMState mOutOfMemoryState;