gmain: don't leak child sources that are destroyed before their parents

A parent source holds refs on its children, so if the child source is
destroyed, we need to drop that ref. Fix, and reorganize to make this
all more obvious.

https://bugzilla.gnome.org/show_bug.cgi?id=682560
This commit is contained in:
Dan Winship 2012-08-23 12:35:20 -04:00
parent 48a9887eae
commit 99c7c951d9

View File

@ -352,6 +352,9 @@ static void g_source_destroy_internal (GSource *source,
static void g_source_set_priority_unlocked (GSource *source, static void g_source_set_priority_unlocked (GSource *source,
GMainContext *context, GMainContext *context,
gint priority); gint priority);
static void g_child_source_remove_internal (GSource *child_source,
GMainContext *context);
static void g_main_context_poll (GMainContext *context, static void g_main_context_poll (GMainContext *context,
gint timeout, gint timeout,
gint priority, gint priority,
@ -1102,7 +1105,7 @@ g_source_destroy_internal (GSource *source,
if (!SOURCE_DESTROYED (source)) if (!SOURCE_DESTROYED (source))
{ {
GSList *sources, *tmp_list; GSList *tmp_list;
gpointer old_cb_data; gpointer old_cb_data;
GSourceCallbackFuncs *old_cb_funcs; GSourceCallbackFuncs *old_cb_funcs;
@ -1131,27 +1134,11 @@ g_source_destroy_internal (GSource *source,
} }
} }
if (source->priv->child_sources) while (source->priv->child_sources)
{ g_child_source_remove_internal (source->priv->child_sources->data, context);
sources = tmp_list = source->priv->child_sources;
source->priv->child_sources = NULL;
while (tmp_list)
{
g_source_destroy_internal (tmp_list->data, context, TRUE);
g_source_unref_internal (tmp_list->data, context, TRUE);
tmp_list = tmp_list->next;
}
g_slist_free (sources);
}
if (source->priv->parent_source) if (source->priv->parent_source)
{ g_child_source_remove_internal (source, context);
GSource *parent = source->priv->parent_source;
parent->priv->child_sources =
g_slist_remove (parent->priv->child_sources, source);
source->priv->parent_source = NULL;
}
g_source_unref_internal (source, context, TRUE); g_source_unref_internal (source, context, TRUE);
} }
@ -1358,6 +1345,20 @@ g_source_add_child_source (GSource *source,
} }
} }
static void
g_child_source_remove_internal (GSource *child_source,
GMainContext *context)
{
GSource *parent_source = child_source->priv->parent_source;
parent_source->priv->child_sources =
g_slist_remove (parent_source->priv->child_sources, child_source);
child_source->priv->parent_source = NULL;
g_source_destroy_internal (child_source, context, TRUE);
g_source_unref_internal (child_source, context, TRUE);
}
/** /**
* g_source_remove_child_source: * g_source_remove_child_source:
* @source:a #GSource * @source:a #GSource
@ -1385,8 +1386,7 @@ g_source_remove_child_source (GSource *source,
if (context) if (context)
LOCK_CONTEXT (context); LOCK_CONTEXT (context);
g_source_destroy_internal (child_source, context, TRUE); g_child_source_remove_internal (child_source, context);
g_source_unref_internal (child_source, context, TRUE);
if (context) if (context)
UNLOCK_CONTEXT (context); UNLOCK_CONTEXT (context);