File gjs-aggressive-gc.patch of Package gjs.9860
diff --git a/gi/object.cpp b/gi/object.cpp
index 74121f8..23a1b4f 100644
--- a/gi/object.cpp
+++ b/gi/object.cpp
@@ -862,12 +862,34 @@ handle_toggle_down(GObject *gobj)
* collected by the GC
*/
if (priv->keep_alive != NULL) {
+ GjsContext *context;
+
gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Removing object from keep alive");
gjs_keep_alive_remove_child(priv->keep_alive,
gobj_no_longer_kept_alive_func,
obj,
priv);
priv->keep_alive = NULL;
+
+ /* During a GC, the collector asks each object which other
+ * objects that it wants to hold on to so if there's an entire
+ * section of the heap graph that's not connected to anything
+ * else, and not reachable from the root set, then it can be
+ * trashed all at once.
+ *
+ * GObjects, however, don't work like that, there's only a
+ * reference count but no notion of who owns the reference so,
+ * a JS object that's proxying a GObject is unconditionally held
+ * alive as long as the GObject has >1 references.
+ *
+ * Since we cannot know how many more wrapped GObjects are going
+ * be marked for garbage collection after the owner is destroyed,
+ * always queue a garbage collection when a toggle reference goes
+ * down.
+ */
+ context = gjs_context_get_current();
+ if (!_gjs_context_destroying(context))
+ _gjs_context_schedule_gc(context);
}
}
diff --git a/gjs/context-private.h b/gjs/context-private.h
index 1d4e6e0..0b6fd58 100644
--- a/gjs/context-private.h
+++ b/gjs/context-private.h
@@ -33,6 +33,8 @@ gboolean _gjs_context_destroying (GjsContext *js_context);
void _gjs_context_schedule_gc_if_needed (GjsContext *js_context);
+void _gjs_context_schedule_gc(GjsContext *js_context);
+
G_END_DECLS
#endif /* __GJS_CONTEXT_PRIVATE_H__ */
diff --git a/gjs/context.cpp b/gjs/context.cpp
index 19120c4..b0cbbd3 100644
--- a/gjs/context.cpp
+++ b/gjs/context.cpp
@@ -70,6 +70,7 @@ struct _GjsContext {
gboolean destroying;
guint auto_gc_id;
+ bool force_gc;
jsid const_strings[GJS_STRING_LAST];
};
@@ -535,14 +536,25 @@ static gboolean
trigger_gc_if_needed (gpointer user_data)
{
GjsContext *js_context = GJS_CONTEXT(user_data);
+
js_context->auto_gc_id = 0;
- gjs_gc_if_needed(js_context->context);
+
+ if (js_context->force_gc)
+ JS_GC(js_context->runtime);
+ else
+ gjs_gc_if_needed(js_context->context);
+
+ js_context->force_gc = FALSE;
+
return FALSE;
}
-void
-_gjs_context_schedule_gc_if_needed (GjsContext *js_context)
+static void
+_gjs_context_schedule_gc_internal (GjsContext *js_context,
+ bool force_gc)
{
+ js_context->force_gc |= force_gc;
+
if (js_context->auto_gc_id > 0)
return;
@@ -551,6 +563,18 @@ _gjs_context_schedule_gc_if_needed (GjsContext *js_context)
js_context, NULL);
}
+void
+_gjs_context_schedule_gc (GjsContext *js_context)
+{
+ _gjs_context_schedule_gc_internal(js_context, TRUE);
+}
+
+void
+_gjs_context_schedule_gc_if_needed (GjsContext *js_context)
+{
+ _gjs_context_schedule_gc_internal(js_context, FALSE);
+}
+
/**
* gjs_context_maybe_gc:
* @context: a #GjsContext