Bug 650161 - Add moving GC callback and use it to fix up ipc CPOW tables r=terrence
authorJon Coppeard <jcoppeard@mozilla.com>
Tue, 02 Sep 2014 11:07:22 +0200
changeset 203053 9df9289e2d8f2709c3365bd8181120ba9b9057fa
parent 203052 888e2521a27704aa7d31acef7b29e5929ca8b768
child 203054 1d84eb49d0eafeee4e3ce9a82fcf80297eb42abf
push id27418
push userryanvm@gmail.com
push dateTue, 02 Sep 2014 18:33:17 +0000
treeherdermozilla-central@7753c52d5976 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs650161
milestone34.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 650161 - Add moving GC callback and use it to fix up ipc CPOW tables r=terrence
js/ipc/JavaScriptChild.cpp
js/ipc/JavaScriptChild.h
js/ipc/JavaScriptParent.cpp
js/ipc/JavaScriptShared.cpp
js/ipc/JavaScriptShared.h
js/src/gc/GCRuntime.h
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsgc.cpp
--- a/js/ipc/JavaScriptChild.cpp
+++ b/js/ipc/JavaScriptChild.cpp
@@ -19,47 +19,55 @@ using namespace mozilla;
 using namespace mozilla::jsipc;
 
 using mozilla::AutoSafeJSContext;
 
 static void
 FinalizeChild(JSFreeOp *fop, JSFinalizeStatus status, bool isCompartment, void *data)
 {
     if (status == JSFINALIZE_GROUP_START) {
-        static_cast<JavaScriptChild *>(data)->finalize(fop);
+        static_cast<JavaScriptChild *>(data)->finalize();
     }
 }
 
+static void
+FixupChildAfterMovingGC(JSRuntime *rt, void *data)
+{
+    static_cast<JavaScriptChild *>(data)->fixupAfterMovingGC();
+}
+
 JavaScriptChild::JavaScriptChild(JSRuntime *rt)
   : JavaScriptShared(rt),
     JavaScriptBase<PJavaScriptChild>(rt)
 {
 }
 
 JavaScriptChild::~JavaScriptChild()
 {
     JS_RemoveFinalizeCallback(rt_, FinalizeChild);
+    JS_RemoveMovingGCCallback(rt_, FixupChildAfterMovingGC);
 }
 
 bool
 JavaScriptChild::init()
 {
     if (!WrapperOwner::init())
         return false;
     if (!WrapperAnswer::init())
         return false;
 
     JS_AddFinalizeCallback(rt_, FinalizeChild, this);
+    JS_AddMovingGCCallback(rt_, FixupChildAfterMovingGC, this);
     return true;
 }
 
 void
-JavaScriptChild::finalize(JSFreeOp *fop)
+JavaScriptChild::finalize()
 {
-    objects_.finalize(fop);
-    objectIds_.finalize(fop);
+    objects_.sweep();
+    objectIds_.sweep();
 }
 
 JSObject *
 JavaScriptChild::defaultScope()
 {
     return xpc::PrivilegedJunkScope();
 }
--- a/js/ipc/JavaScriptChild.h
+++ b/js/ipc/JavaScriptChild.h
@@ -16,17 +16,17 @@ namespace jsipc {
 
 class JavaScriptChild : public JavaScriptBase<PJavaScriptChild>
 {
   public:
     explicit JavaScriptChild(JSRuntime *rt);
     virtual ~JavaScriptChild();
 
     bool init();
-    void finalize(JSFreeOp *fop);
+    void finalize();
 
     void drop(JSObject *obj);
 
   protected:
     virtual bool isParent() { return false; }
     virtual JSObject *defaultScope() MOZ_OVERRIDE;
 
   private:
--- a/js/ipc/JavaScriptParent.cpp
+++ b/js/ipc/JavaScriptParent.cpp
@@ -22,34 +22,42 @@ using namespace mozilla::jsipc;
 using namespace mozilla::dom;
 
 static void
 TraceParent(JSTracer *trc, void *data)
 {
     static_cast<JavaScriptParent *>(data)->trace(trc);
 }
 
+static void
+FixupParentAfterMovingGC(JSRuntime *rt, void *data)
+{
+    static_cast<JavaScriptParent *>(data)->fixupAfterMovingGC();
+}
+
 JavaScriptParent::JavaScriptParent(JSRuntime *rt)
   : JavaScriptShared(rt),
     JavaScriptBase<PJavaScriptParent>(rt)
 {
 }
 
 JavaScriptParent::~JavaScriptParent()
 {
     JS_RemoveExtraGCRootsTracer(rt_, TraceParent, this);
+    JS_RemoveMovingGCCallback(rt_, FixupParentAfterMovingGC);
 }
 
 bool
 JavaScriptParent::init()
 {
     if (!WrapperOwner::init())
         return false;
 
     JS_AddExtraGCRootsTracer(rt_, TraceParent, this);
+    JS_AddMovingGCCallback(rt_, FixupParentAfterMovingGC, this);
     return true;
 }
 
 void
 JavaScriptParent::trace(JSTracer *trc)
 {
     if (active())
         objects_.trace(trc);
--- a/js/ipc/JavaScriptShared.cpp
+++ b/js/ipc/JavaScriptShared.cpp
@@ -36,24 +36,22 @@ IdToObjectMap::trace(JSTracer *trc)
     for (Table::Range r(table_.all()); !r.empty(); r.popFront()) {
         DebugOnly<JSObject *> prior = r.front().value().get();
         JS_CallObjectTracer(trc, &r.front().value(), "ipc-object");
         MOZ_ASSERT(r.front().value() == prior);
     }
 }
 
 void
-IdToObjectMap::finalize(JSFreeOp *fop)
+IdToObjectMap::sweep()
 {
     for (Table::Enum e(table_); !e.empty(); e.popFront()) {
         DebugOnly<JSObject *> prior = e.front().value().get();
         if (JS_IsAboutToBeFinalized(&e.front().value()))
             e.removeFront();
-        else
-            MOZ_ASSERT(e.front().value() == prior);
     }
 }
 
 JSObject *
 IdToObjectMap::find(ObjectId id)
 {
     Table::Ptr p = table_.lookup(id);
     if (!p)
@@ -92,24 +90,24 @@ ObjectToIdMap::init()
     if (table_)
         return true;
 
     table_ = new Table(SystemAllocPolicy());
     return table_ && table_->init(32);
 }
 
 void
-ObjectToIdMap::finalize(JSFreeOp *fop)
+ObjectToIdMap::sweep()
 {
     for (Table::Enum e(*table_); !e.empty(); e.popFront()) {
         JSObject *obj = e.front().key();
         if (JS_IsAboutToBeFinalizedUnbarriered(&obj))
             e.removeFront();
-        else
-            MOZ_ASSERT(obj == e.front().key());
+        else if (obj != e.front().key())
+            e.rekeyFront(obj);
     }
 }
 
 ObjectId
 ObjectToIdMap::find(JSObject *obj)
 {
     Table::Ptr p = table_->lookup(obj);
     if (!p)
@@ -591,8 +589,14 @@ JavaScriptShared::Wrap(JSContext *cx, Ha
             return false;
 
         outCpows->AppendElement(CpowEntry(str, var));
     }
 
     return true;
 }
 
+void JavaScriptShared::fixupAfterMovingGC()
+{
+    objects_.sweep();
+    cpows_.sweep();
+    objectIds_.sweep();
+}
--- a/js/ipc/JavaScriptShared.h
+++ b/js/ipc/JavaScriptShared.h
@@ -43,43 +43,47 @@ class IdToObjectMap
 
     typedef js::HashMap<ObjectId, JS::Heap<JSObject *>, TableKeyHasher, js::SystemAllocPolicy> Table;
 
   public:
     IdToObjectMap();
 
     bool init();
     void trace(JSTracer *trc);
-    void finalize(JSFreeOp *fop);
+    void sweep();
 
     bool add(ObjectId id, JSObject *obj);
     JSObject *find(ObjectId id);
     void remove(ObjectId id);
 
+    void fixupAfterMovingGC();
+
   private:
     Table table_;
 };
 
 // Map JSObjects -> ids
 class ObjectToIdMap
 {
     typedef js::PointerHasher<JSObject *, 3> Hasher;
     typedef js::HashMap<JSObject *, ObjectId, Hasher, js::SystemAllocPolicy> Table;
 
   public:
     ObjectToIdMap();
     ~ObjectToIdMap();
 
     bool init();
-    void finalize(JSFreeOp *fop);
+    void sweep();
 
     bool add(JSContext *cx, JSObject *obj, ObjectId id);
     ObjectId find(JSObject *obj);
     void remove(JSObject *obj);
 
+    void fixupAfterMovingGC();
+
   private:
     static void keyMarkCallback(JSTracer *trc, JSObject *key, void *data);
 
     Table *table_;
 };
 
 class Logging;
 
@@ -95,16 +99,18 @@ class JavaScriptShared
     void incref();
 
     static const uint32_t OBJECT_EXTRA_BITS  = 1;
     static const uint32_t OBJECT_IS_CALLABLE = (1 << 0);
 
     bool Unwrap(JSContext *cx, const InfallibleTArray<CpowEntry> &aCpows, JS::MutableHandleObject objp);
     bool Wrap(JSContext *cx, JS::HandleObject aObj, InfallibleTArray<CpowEntry> *outCpows);
 
+    void fixupAfterMovingGC();
+
   protected:
     bool toVariant(JSContext *cx, JS::HandleValue from, JSVariant *to);
     bool fromVariant(JSContext *cx, const JSVariant &from, JS::MutableHandleValue to);
 
     bool fromDescriptor(JSContext *cx, JS::Handle<JSPropertyDescriptor> desc,
                         PPropertyDescriptor *out);
     bool toDescriptor(JSContext *cx, const PPropertyDescriptor &in,
                       JS::MutableHandle<JSPropertyDescriptor> out);
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -409,16 +409,18 @@ class GCRuntime
     void resetMallocBytes();
     bool isTooMuchMalloc() const { return mallocBytes <= 0; }
     void updateMallocCounter(JS::Zone *zone, size_t nbytes);
     void onTooMuchMalloc();
 
     void setGCCallback(JSGCCallback callback, void *data);
     bool addFinalizeCallback(JSFinalizeCallback callback, void *data);
     void removeFinalizeCallback(JSFinalizeCallback func);
+    bool addMovingGCCallback(JSMovingGCCallback callback, void *data);
+    void removeMovingGCCallback(JSMovingGCCallback func);
     JS::GCSliceCallback setSliceCallback(JS::GCSliceCallback callback);
 
     void setValidate(bool enable);
     void setFullCompartmentChecks(bool enable);
 
     bool isManipulatingDeadZones() { return manipulatingDeadZones; }
     void setManipulatingDeadZones(bool value) { manipulatingDeadZones = value; }
     unsigned objectsMarkedInDeadZonesCount() { return objectsMarkedInDeadZones; }
@@ -792,16 +794,17 @@ class GCRuntime
     js::Vector<JSObject *, 0, js::SystemAllocPolicy>   selectedForMarking;
 #endif
 
     bool                  validate;
     bool                  fullCompartmentChecks;
 
     Callback<JSGCCallback>  gcCallback;
     CallbackVector<JSFinalizeCallback> finalizeCallbacks;
+    CallbackVector<JSMovingGCCallback> movingCallbacks;
 
     /*
      * Malloc counter to measure memory pressure for GC scheduling. It runs
      * from   maxMallocBytes down to zero.
      */
     mozilla::Atomic<ptrdiff_t, mozilla::ReleaseAcquire>   mallocBytes;
 
     /*
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1884,16 +1884,29 @@ JS_AddFinalizeCallback(JSRuntime *rt, JS
 
 JS_PUBLIC_API(void)
 JS_RemoveFinalizeCallback(JSRuntime *rt, JSFinalizeCallback cb)
 {
     rt->gc.removeFinalizeCallback(cb);
 }
 
 JS_PUBLIC_API(bool)
+JS_AddMovingGCCallback(JSRuntime *rt, JSMovingGCCallback cb, void *data)
+{
+    AssertHeapIsIdle(rt);
+    return rt->gc.addMovingGCCallback(cb, data);
+}
+
+JS_PUBLIC_API(void)
+JS_RemoveMovingGCCallback(JSRuntime *rt, JSMovingGCCallback cb)
+{
+    rt->gc.removeMovingGCCallback(cb);
+}
+
+JS_PUBLIC_API(bool)
 JS_IsAboutToBeFinalized(JS::Heap<JSObject *> *objp)
 {
     return IsObjectAboutToBeFinalized(objp->unsafeGet());
 }
 
 JS_PUBLIC_API(bool)
 JS_IsAboutToBeFinalizedUnbarriered(JSObject **objp)
 {
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -684,16 +684,19 @@ typedef enum JSFinalizeStatus {
      * Called at the end of collection when everything has been swept.
      */
     JSFINALIZE_COLLECTION_END
 } JSFinalizeStatus;
 
 typedef void
 (* JSFinalizeCallback)(JSFreeOp *fop, JSFinalizeStatus status, bool isCompartment, void *data);
 
+typedef void
+(* JSMovingGCCallback)(JSRuntime *rt, void *data);
+
 typedef bool
 (* JSInterruptCallback)(JSContext *cx);
 
 typedef void
 (* JSErrorReporter)(JSContext *cx, const char *message, JSErrorReport *report);
 
 #ifdef MOZ_TRACE_JSCALLS
 typedef void
@@ -2021,16 +2024,22 @@ JS_SetGCCallback(JSRuntime *rt, JSGCCall
 
 extern JS_PUBLIC_API(bool)
 JS_AddFinalizeCallback(JSRuntime *rt, JSFinalizeCallback cb, void *data);
 
 extern JS_PUBLIC_API(void)
 JS_RemoveFinalizeCallback(JSRuntime *rt, JSFinalizeCallback cb);
 
 extern JS_PUBLIC_API(bool)
+JS_AddMovingGCCallback(JSRuntime *rt, JSMovingGCCallback cb, void *data);
+
+extern JS_PUBLIC_API(void)
+JS_RemoveMovingGCCallback(JSRuntime *rt, JSMovingGCCallback cb);
+
+extern JS_PUBLIC_API(bool)
 JS_IsGCMarkingTracer(JSTracer *trc);
 
 /* For assertions only. */
 #ifdef JS_DEBUG
 extern JS_PUBLIC_API(bool)
 JS_IsMarkingGray(JSTracer *trc);
 #endif
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1590,16 +1590,35 @@ GCRuntime::removeFinalizeCallback(JSFina
          p < finalizeCallbacks.end(); p++) {
         if (p->op == callback) {
             finalizeCallbacks.erase(p);
             break;
         }
     }
 }
 
+bool
+GCRuntime::addMovingGCCallback(JSMovingGCCallback callback, void *data)
+{
+    return movingCallbacks.append(Callback<JSMovingGCCallback>(callback, data));
+}
+
+void
+GCRuntime::removeMovingGCCallback(JSMovingGCCallback callback)
+{
+    for (Callback<JSMovingGCCallback> *p = movingCallbacks.begin();
+         p < movingCallbacks.end(); p++)
+    {
+        if (p->op == callback) {
+            movingCallbacks.erase(p);
+            break;
+        }
+    }
+}
+
 JS::GCSliceCallback
 GCRuntime::setSliceCallback(JS::GCSliceCallback callback) {
     return stats.setSliceCallback(callback);
 }
 
 template <typename T>
 bool
 GCRuntime::addRoot(T *rp, const char *name, JSGCRootType rootType)
@@ -2421,16 +2440,23 @@ GCRuntime::updatePointersToRelocatedCell
     }
 
     // Mark all gray roots, making sure we call the trace callback to get the
     // current set.
     if (JSTraceDataOp op = grayRootTracer.op)
         (*op)(&trc, grayRootTracer.data);
 
     MovingTracer::Sweep(&trc);
+
+    // Call callbacks to get the rest of the system to fixup other untraced pointers.
+    for (Callback<JSMovingGCCallback> *p = rt->gc.movingCallbacks.begin();
+         p < rt->gc.movingCallbacks.end(); p++)
+    {
+        p->op(rt, p->data);
+    }
 }
 
 void
 GCRuntime::releaseRelocatedArenas(ArenaHeader *relocatedList)
 {
     // Release the relocated arenas, now containing only forwarding pointers
 
 #ifdef DEBUG