File gjs-aggressive-gc.patch of Package gjs.11531
diff --git a/gi/object.cpp b/gi/object.cpp
index e77de88..57816d9 100644
--- a/gi/object.cpp
+++ b/gi/object.cpp
@@ -1010,9 +1010,31 @@ handle_toggle_down(GObject *gobj)
* collected by the GC
*/
if (priv->keep_alive.rooted()) {
+ GjsContext *context;
+
gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Removing object from keep alive");
priv->keep_alive.switch_to_unrooted();
dissociate_list_remove(priv);
+
+ /* 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 6dbe669..2283d01 100644
--- a/gjs/context-private.h
+++ b/gjs/context-private.h
@@ -57,6 +57,8 @@ bool _gjs_context_run_jobs(GjsContext *gjs_context);
void _gjs_context_unregister_unhandled_promise_rejection(GjsContext *gjs_context,
uint64_t promise_id);
+void _gjs_context_schedule_gc(GjsContext *js_context);
+
G_END_DECLS
void _gjs_context_register_unhandled_promise_rejection(GjsContext *gjs_context,
diff --git a/gjs/context.cpp b/gjs/context.cpp
index c105118..4d12e0c 100644
--- a/gjs/context.cpp
+++ b/gjs/context.cpp
@@ -85,6 +85,7 @@ struct _GjsContext {
uint8_t exit_code;
guint auto_gc_id;
+ bool force_gc;
std::array<JS::PersistentRootedId*, GJS_STRING_LAST> const_strings;
@@ -422,14 +423,24 @@ 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->context);
+ else
+ gjs_gc_if_needed(js_context->context);
+
+ js_context->force_gc = FALSE;
return G_SOURCE_REMOVE;
}
-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;
@@ -606,6 +617,18 @@ _gjs_context_unregister_unhandled_promise_rejection(GjsContext *gjs_context,
"previously marked as unhandled", erased == 1));
}
+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