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
openSUSE Build Service is sponsored by