Bug 878160 - GC: post barrier weak references in the browser - part 2 browser r=terrence r=billm
authorTerrence Cole <terrence@mozilla.com>
Wed, 05 Jun 2013 16:40:02 -0700
changeset 150286 d1f08b7f90b7876fb505d1334d0b9fcdd18da5bb
parent 150285 0ef38d43fd498675d694c4a5bc1e2b5cef7db887
child 150287 3184e72d56af09d271a16b529301afda9a48d873
push id382
push userakeybl@mozilla.com
push dateMon, 21 Oct 2013 21:47:13 +0000
treeherdermozilla-release@5f1868ee45cb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence, billm
bugs878160
milestone25.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 878160 - GC: post barrier weak references in the browser - part 2 browser r=terrence r=billm
js/public/GCAPI.h
js/src/jsapi.cpp
js/src/jsapi.h
js/xpconnect/src/XPCMaps.h
js/xpconnect/src/XPCWrappedJS.cpp
js/xpconnect/src/XPCWrappedNative.cpp
js/xpconnect/src/xpcprivate.h
js/xpconnect/wrappers/WrapperFactory.cpp
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -200,17 +200,17 @@ extern JS_FRIEND_API(void)
 PokeGC(JSRuntime *rt);
 
 /* Was the most recent GC run incrementally? */
 extern JS_FRIEND_API(bool)
 WasIncrementalGC(JSRuntime *rt);
 
 class ObjectPtr
 {
-    JSObject *value;
+    Heap<JSObject *> value;
 
   public:
     ObjectPtr() : value(NULL) {}
 
     ObjectPtr(JSObject *obj) : value(obj) {}
 
     /* Always call finalize before the destructor. */
     ~ObjectPtr() { JS_ASSERT(!value); }
@@ -235,17 +235,17 @@ class ObjectPtr
 
     ObjectPtr &operator=(JSObject *obj) {
         IncrementalObjectBarrier(value);
         value = obj;
         return *this;
     }
 
     void trace(JSTracer *trc, const char *name) {
-        JS_CallObjectTracer(trc, &value, name);
+        JS_CallHeapObjectTracer(trc, &value, name);
     }
 
     JSObject &operator*() const { return *value; }
     JSObject *operator->() const { return value; }
     operator JSObject *() const { return value; }
 };
 
 /*
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2784,19 +2784,25 @@ JS_SetGCCallback(JSRuntime *rt, JSGCCall
 JS_PUBLIC_API(void)
 JS_SetFinalizeCallback(JSRuntime *rt, JSFinalizeCallback cb)
 {
     AssertHeapIsIdle(rt);
     rt->gcFinalizeCallback = cb;
 }
 
 JS_PUBLIC_API(JSBool)
-JS_IsAboutToBeFinalized(JSObject **obj)
-{
-    return IsObjectAboutToBeFinalized(obj);
+JS_IsAboutToBeFinalized(JS::Heap<JSObject *> *objp)
+{
+    return IsObjectAboutToBeFinalized(objp->unsafeGet());
+}
+
+JS_PUBLIC_API(JSBool)
+JS_IsAboutToBeFinalizedUnbarriered(JSObject **objp)
+{
+    return IsObjectAboutToBeFinalized(objp);
 }
 
 JS_PUBLIC_API(void)
 JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32_t value)
 {
     switch (key) {
       case JSGC_MAX_BYTES: {
         JS_ASSERT(value >= rt->gcBytes);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2647,17 +2647,20 @@ JS_IsGCMarkingTracer(JSTracer *trc);
  * garbage collector might have moved it. In this case, the reference passed
  * to JS_IsAboutToBeFinalized will be updated to the object's new location.
  * Callers of this method are responsible for updating any state that is
  * dependent on the object's address. For example, if the object's address is
  * used as a key in a hashtable, then the object must be removed and
  * re-inserted with the correct hash.
  */
 extern JS_PUBLIC_API(JSBool)
-JS_IsAboutToBeFinalized(JSObject **obj);
+JS_IsAboutToBeFinalized(JS::Heap<JSObject *> *objp);
+
+extern JS_PUBLIC_API(JSBool)
+JS_IsAboutToBeFinalizedUnbarriered(JSObject **objp);
 
 typedef enum JSGCParamKey {
     /* Maximum nominal heap before last ditch GC. */
     JSGC_MAX_BYTES          = 0,
 
     /* Number of JS_malloc bytes before last ditch GC. */
     JSGC_MAX_MALLOC_BYTES   = 1,
 
--- a/js/xpconnect/src/XPCMaps.h
+++ b/js/xpconnect/src/XPCMaps.h
@@ -7,16 +7,17 @@
 /* Private maps (hashtables). */
 
 #ifndef xpcmaps_h___
 #define xpcmaps_h___
 
 #include "mozilla/MemoryReporting.h"
 
 #include "js/HashTable.h"
+#include "jsfriendapi.h"
 
 // Maps...
 
 // Note that most of the declarations for hash table entries begin with
 // a pointer to something or another. This makes them look enough like
 // the PLDHashEntryStub struct that the default OPs (PL_DHashGetStubOps())
 // just do the right thing for most of our needs.
 
@@ -40,23 +41,26 @@ public:
     }
 
     inline nsXPCWrappedJS* Find(JSObject* Obj) {
         NS_PRECONDITION(Obj,"bad param");
         Map::Ptr p = mTable.lookup(Obj);
         return p ? p->value : nullptr;
     }
 
-    inline nsXPCWrappedJS* Add(nsXPCWrappedJS* wrapper) {
+    inline nsXPCWrappedJS* Add(JSContext* cx, nsXPCWrappedJS* wrapper) {
         NS_PRECONDITION(wrapper,"bad param");
         JSObject* obj = wrapper->GetJSObjectPreserveColor();
         Map::AddPtr p = mTable.lookupForAdd(obj);
         if (p)
             return p->value;
-        return mTable.add(p, obj, wrapper) ? wrapper : nullptr;
+        if (!mTable.add(p, obj, wrapper))
+            return nullptr;
+        JS_StorePostBarrierCallback(cx, KeyMarkCallback, obj);
+        return wrapper;
     }
 
     inline void Remove(nsXPCWrappedJS* wrapper) {
         NS_PRECONDITION(wrapper,"bad param");
         mTable.remove(wrapper->GetJSObjectPreserveColor());
     }
 
     inline uint32_t Count() {return mTable.count();}
@@ -74,16 +78,29 @@ public:
         size_t n = mallocSizeOf(this);
         n += mTable.sizeOfExcludingThis(mallocSizeOf);
         return n;
     }
 
 private:
     JSObject2WrappedJSMap() {}
 
+    /*
+     * This function is called during minor GCs for each key in the HashMap that
+     * has been moved.
+     */
+    static void KeyMarkCallback(JSTracer *trc, void *k) {
+        JSObject *key = static_cast<JSObject*>(k);
+        JSObject *prior = key;
+        JS_CallObjectTracer(trc, &key, "XPCJSRuntime::mWrappedJSMap key");
+        XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
+        JSObject2WrappedJSMap* self = rt->GetWrappedJSMap();
+        self->mTable.rekey(prior, key);
+    }
+
     Map mTable;
 };
 
 /*************************/
 
 class Native2WrappedNativeMap
 {
 public:
@@ -600,17 +617,17 @@ private:
 private:
     PLDHashTable *mTable;
 };
 
 /***************************************************************************/
 
 class JSObject2JSObjectMap
 {
-    typedef js::HashMap<JSObject *, JSObject *, js::PointerHasher<JSObject *, 3>,
+    typedef js::HashMap<JSObject *, JS::Heap<JSObject *>, js::PointerHasher<JSObject *, 3>,
                         js::SystemAllocPolicy> Map;
 
 public:
     static JSObject2JSObjectMap* newMap(int size) {
         JSObject2JSObjectMap* map = new JSObject2JSObjectMap();
         if (map && map->mTable.init(size))
             return map;
         delete map;
@@ -620,37 +637,39 @@ public:
     inline JSObject* Find(JSObject* key) {
         NS_PRECONDITION(key, "bad param");
         if (Map::Ptr p = mTable.lookup(key))
             return p->value;
         return nullptr;
     }
 
     /* Note: If the entry already exists, return the old value. */
-    inline JSObject* Add(JSObject *key, JSObject *value) {
+    inline JSObject* Add(JSContext *cx, JSObject *key, JSObject *value) {
         NS_PRECONDITION(key,"bad param");
         Map::AddPtr p = mTable.lookupForAdd(key);
         if (p)
             return p->value;
         if (!mTable.add(p, key, value))
             return nullptr;
+        MOZ_ASSERT(xpc::GetObjectScope(key)->mWaiverWrapperMap == this);
+        JS_StorePostBarrierCallback(cx, KeyMarkCallback, key);
         return value;
     }
 
     inline void Remove(JSObject* key) {
         NS_PRECONDITION(key,"bad param");
         mTable.remove(key);
     }
 
     inline uint32_t Count() { return mTable.count(); }
 
     void Sweep() {
         for (Map::Enum e(mTable); !e.empty(); e.popFront()) {
             JSObject *updated = e.front().key;
-            if (JS_IsAboutToBeFinalized(&updated) || JS_IsAboutToBeFinalized(&e.front().value))
+            if (JS_IsAboutToBeFinalizedUnbarriered(&updated) || JS_IsAboutToBeFinalized(&e.front().value))
                 e.removeFront();
             else if (updated != e.front().key)
                 e.rekeyFront(updated);
         }
     }
 
     void Reparent(JSContext *aCx, JSObject *aNewInnerArg) {
         JS::RootedObject aNewInner(aCx, aNewInnerArg);
@@ -669,12 +688,24 @@ public:
                 JS_ClearPendingException(aCx);
             }
         }
     }
 
 private:
     JSObject2JSObjectMap() {}
 
+    /*
+     * This function is called during minor GCs for each key in the HashMap that
+     * has been moved.
+     */
+    static void KeyMarkCallback(JSTracer *trc, void *k) {
+        JSObject *key = static_cast<JSObject*>(k);
+        JSObject *prior = key;
+        JS_CallObjectTracer(trc, &key, "XPCWrappedNativeScope::mWaiverWrapperMap key");
+        JSObject2JSObjectMap *self = xpc::GetObjectScope(key)->mWaiverWrapperMap;
+        self->mTable.rekey(prior, key);
+    }
+
     Map mTable;
 };
 
 #endif /* xpcmaps_h___ */
--- a/js/xpconnect/src/XPCWrappedJS.cpp
+++ b/js/xpconnect/src/XPCWrappedJS.cpp
@@ -217,17 +217,17 @@ do_decrement:
     return cnt;
 }
 
 void
 nsXPCWrappedJS::TraceJS(JSTracer* trc)
 {
     NS_ASSERTION(mRefCnt >= 2 && IsValid(), "must be strongly referenced");
     JS_SET_TRACING_DETAILS(trc, GetTraceName, this, 0);
-    JS_CallObjectTracer(trc, &mJSObj, "nsXPCWrappedJS::mJSObj");
+    JS_CallHeapObjectTracer(trc, &mJSObj, "nsXPCWrappedJS::mJSObj");
 }
 
 // static
 void
 nsXPCWrappedJS::GetTraceName(JSTracer* trc, char *buf, size_t bufsize)
 {
     const nsXPCWrappedJS* self = static_cast<const nsXPCWrappedJS*>
                                             (trc->debugPrintArg);
@@ -333,17 +333,17 @@ nsXPCWrappedJS::GetNewOrUsed(JSObject* a
                 goto return_wrapper;
 
             {   // scoped lock
 #if DEBUG_xpc_leaks
                 printf("Created nsXPCWrappedJS %p, JSObject is %p\n",
                        (void*)wrapper, (void*)jsObj);
 #endif
                 XPCAutoLock lock(rt->GetMapLock());
-                map->Add(root);
+                map->Add(cx, root);
             }
 
             if (!CheckMainThreadOnly(root)) {
                 XPCAutoLock lock(rt->GetMapLock());
                 map->Remove(root);
 
                 wrapper = NULL;
             }
@@ -366,17 +366,17 @@ nsXPCWrappedJS::GetNewOrUsed(JSObject* a
             release_root = true;
 
             {   // scoped lock
 #if DEBUG_xpc_leaks
                 printf("Created nsXPCWrappedJS %p, JSObject is %p\n",
                        (void*)root, (void*)rootJSObj);
 #endif
                 XPCAutoLock lock(rt->GetMapLock());
-                map->Add(root);
+                map->Add(cx, root);
             }
 
             if (!CheckMainThreadOnly(root)) {
                 XPCAutoLock lock(rt->GetMapLock());
                 map->Remove(root);
 
                 goto return_wrapper;
             }
@@ -650,17 +650,17 @@ NS_IMETHODIMP
 nsXPCWrappedJS::DebugDump(int16_t depth)
 {
 #ifdef DEBUG
     XPC_LOG_ALWAYS(("nsXPCWrappedJS @ %x with mRefCnt = %d", this, mRefCnt.get()));
         XPC_LOG_INDENT();
 
         bool isRoot = mRoot == this;
         XPC_LOG_ALWAYS(("%s wrapper around JSObject @ %x", \
-                        isRoot ? "ROOT":"non-root", mJSObj));
+                        isRoot ? "ROOT":"non-root", mJSObj.get()));
         char* name;
         GetClass()->GetInterfaceInfo()->GetName(&name);
         XPC_LOG_ALWAYS(("interface name is %s", name));
         if (name)
             nsMemory::Free(name);
         char * iid = GetClass()->GetIID().ToString();
         XPC_LOG_ALWAYS(("IID number is %s", iid ? iid : "invalid"));
         if (iid)
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -1148,17 +1148,17 @@ XPCWrappedNative::FlatJSObjectFinalized(
     // JSObjects are about to be finalized too.
 
     XPCWrappedNativeTearOffChunk* chunk;
     for (chunk = &mFirstChunk; chunk; chunk = chunk->mNextChunk) {
         XPCWrappedNativeTearOff* to = chunk->mTearOffs;
         for (int i = XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK-1; i >= 0; i--, to++) {
             JSObject* jso = to->GetJSObjectPreserveColor();
             if (jso) {
-                NS_ASSERTION(JS_IsAboutToBeFinalized(&jso), "bad!");
+                MOZ_ASSERT(JS_IsAboutToBeFinalizedUnbarriered(&jso));
                 JS_SetPrivate(jso, nullptr);
                 to->JSObjectFinalized();
             }
 
             // We also need to release any native pointers held...
             nsISupports* obj = to->GetNative();
             if (obj) {
 #ifdef XP_WIN
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -2835,17 +2835,17 @@ protected:
                    JSObject* aJSObj,
                    nsXPCWrappedJSClass* aClass,
                    nsXPCWrappedJS* root,
                    nsISupports* aOuter);
 
    void Unlink();
 
 private:
-    JSObject* mJSObj;
+    JS::Heap<JSObject*> mJSObj;
     nsXPCWrappedJSClass* mClass;
     nsXPCWrappedJS* mRoot;
     nsXPCWrappedJS* mNext;
     nsISupports* mOuter;    // only set in root
     bool mMainThread;
     bool mMainThreadOnly;
 };
 
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -88,17 +88,17 @@ WrapperFactory::CreateXrayWaiver(JSConte
 
     // Add the new waiver to the map. It's important that we only ever have
     // one waiver for the lifetime of the target object.
     if (!scope->mWaiverWrapperMap) {
         scope->mWaiverWrapperMap =
           JSObject2JSObjectMap::newMap(XPC_WRAPPER_MAP_SIZE);
         MOZ_ASSERT(scope->mWaiverWrapperMap);
     }
-    if (!scope->mWaiverWrapperMap->Add(obj, waiver))
+    if (!scope->mWaiverWrapperMap->Add(cx, obj, waiver))
         return nullptr;
     return waiver;
 }
 
 JSObject *
 WrapperFactory::WaiveXray(JSContext *cx, JSObject *objArg)
 {
     RootedObject obj(cx, objArg);