[gjs/wip/ptomato/786668: 6/6] closure: Prevent collection of invalidated closure



commit 46ac55e7ebd6d52eff621f149baf02ed2e8f0455
Author: Philip Chimento <philip chimento gmail com>
Date:   Tue Aug 22 22:58:56 2017 -0700

    closure: Prevent collection of invalidated closure
    
    It's not possible to stop tracing an object in the middle of GC. However,
    by using JS::ExposeObjectToActiveJS(), it is possible to mark an object
    as reachable for the duration of one GC. This is exactly what we need for
    closures, to keep the closure's callable object from disappearing while
    GC is going on.
    
    This requires adding a method, prevent_collection(), to GjsMaybeOwned.
    This method is only valid in non-rooted mode. It also requires a bit of
    GC API magic to avoid running afoul of some debug-mode assertions.
    SpiderMonkey master at least has an official API for one of these bits of
    magic, which we can switch to in SpiderMonkey 59.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=786668

 gi/closure.cpp        |    1 +
 gjs/jsapi-util-root.h |   24 ++++++++++++++++++++++++
 2 files changed, 25 insertions(+), 0 deletions(-)
---
diff --git a/gi/closure.cpp b/gi/closure.cpp
index 2528b13..b92e22c 100644
--- a/gi/closure.cpp
+++ b/gi/closure.cpp
@@ -169,6 +169,7 @@ closure_set_invalid(gpointer  data,
     gjs_debug_closure("Invalidating signal closure %p which calls object %p",
                       closure, self->obj.get());
 
+    self->obj.prevent_collection();
     self->obj.reset();
     self->context = nullptr;
 
diff --git a/gjs/jsapi-util-root.h b/gjs/jsapi-util-root.h
index 33f748d..5baed48 100644
--- a/gjs/jsapi-util-root.h
+++ b/gjs/jsapi-util-root.h
@@ -67,6 +67,7 @@
 template<typename T>
 struct GjsHeapOperation {
     static bool update_after_gc(JS::Heap<T> *location);
+    static void expose_to_js(JS::Heap<T>& thing);
 };
 
 template<>
@@ -77,6 +78,18 @@ struct GjsHeapOperation<JSObject *> {
         JS_UpdateWeakPointerAfterGC(location);
         return (location->unbarrieredGet() == nullptr);
     }
+
+    static void expose_to_js(JS::Heap<JSObject *>& thing) {
+        JSObject *obj = thing.unbarrieredGet();
+        /* If the object has been swept already, then the zone is nullptr */
+        if (!obj || !js::gc::detail::GetGCThingZone(uintptr_t(obj)))
+            return;
+        /* COMPAT: Use JS::CurrentThreadIsHeapCollecting() in mozjs59 */
+        JS::GCCellPtr ptr(obj, JS::TraceKind::Object);
+        JS::shadow::Runtime *rt = js::gc::detail::GetCellRuntime(ptr.asCell());
+        if (!rt->isHeapCollecting())
+            JS::ExposeObjectToActiveJS(obj);
+    }
 };
 
 template<>
@@ -249,6 +262,17 @@ public:
         m_heap = thing;
     }
 
+    /* Marks an object as reachable for one GC with ExposeObjectToActiveJS().
+     * Use to avoid stopping tracing an object during GC. This makes no sense
+     * in the rooted case. */
+    void
+    prevent_collection(void)
+    {
+        debug("prevent_collection()");
+        g_assert(!m_rooted);
+        GjsHeapOperation<T>::expose_to_js(m_heap);
+    }
+
     void
     reset(void)
     {


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]