object: Queue a forced GC when toggling down

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.

Issue: #140
parent 09029851
Pipeline #9185 passed with stages
in 34 minutes and 12 seconds
......@@ -1001,8 +1001,30 @@ handle_toggle_down(GObject *gobj)
* collected by the GC
*/
if (priv->keep_alive.rooted()) {
GjsContext *context;
gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Unrooting object");
priv->keep_alive.switch_to_unrooted();
/* 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);
}
}
......
......@@ -36,7 +36,7 @@ bool _gjs_context_destroying (GjsContext *js_context);
void _gjs_context_schedule_gc_if_needed (GjsContext *js_context);
void _gjs_context_schedule_gc (GjsContext *js_context);
void _gjs_context_schedule_gc(GjsContext *js_context);
void _gjs_context_exit(GjsContext *js_context,
uint8_t exit_code);
......
......@@ -599,31 +599,33 @@ trigger_gc_if_needed (gpointer user_data)
else
gjs_gc_if_needed(js_context->context);
js_context->force_gc = false;
return G_SOURCE_REMOVE;
}
static void
_gjs_context_schedule_gc_internal (GjsContext *js_context,
bool force_gc)
_gjs_context_schedule_gc_internal(GjsContext *js_context,
bool force_gc)
{
if (js_context->auto_gc_id > 0)
g_source_remove(js_context->auto_gc_id);
return;
js_context->force_gc = force_gc;
js_context->force_gc |= force_gc;
js_context->auto_gc_id = g_idle_add_full(G_PRIORITY_LOW,
trigger_gc_if_needed,
js_context, NULL);
}
void
_gjs_context_schedule_gc (GjsContext *js_context)
_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_if_needed(GjsContext *js_context)
{
_gjs_context_schedule_gc_internal(js_context, false);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment