[telepathy-gabble/master] Don't ref media channel when waiting for relay
Will Thompson
will.thompson at collabora.co.uk
Wed Aug 19 08:43:07 PDT 2009
When we look up relay information from Google for new media streams, we
use a StreamCreationData * as the user_data for the libsoup HTTP request.
You can't cancel those requests, so the StreamCreationData can outlive the
call.
But currently, it holds a reference to the channel. This means that the
media channel can outlive the connection, which is bad for several
reasons:
* It's still on the bus, so if you make a new connection and it happens
to get the same path as the old one, then make a new call which happens
to get the same path as this channel, we assert.
* When the request does come back (or fails because we destroyed the
SoupSession — this happens asynchronously), the channel's disposed. Its
dispose() tries to call methods on the GabbleConnection, which is no
longer valid, and we crash.
Instead, StreamCreationData now takes a weak ref to the channel. If the
channel's disposed, that field of the closure is set to NULL; the relevant
callbacks react to 'channel' being NULL by doing nothing, just as they do
if 'content' is NULL.
---
src/media-channel.c | 58 +++++++++++++++++++++++++++++++-------------------
1 files changed, 36 insertions(+), 22 deletions(-)
diff --git a/src/media-channel.c b/src/media-channel.c
index d388f3b..7b73d5d 100644
--- a/src/media-channel.c
+++ b/src/media-channel.c
@@ -823,6 +823,7 @@ gabble_media_channel_dispose (GObject *object)
TpBaseConnection *conn = (TpBaseConnection *) priv->conn;
TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (
conn, TP_HANDLE_TYPE_CONTACT);
+ GList *l;
if (priv->dispose_has_run)
return;
@@ -831,10 +832,20 @@ gabble_media_channel_dispose (GObject *object)
priv->dispose_has_run = TRUE;
- /* StreamCreationData * holds a reference to the media channel; thus, we
- * shouldn't be disposed till they've all gone away.
+ if (!priv->closed)
+ gabble_media_channel_close (self);
+
+ g_assert (priv->closed);
+ g_assert (priv->session == NULL);
+
+ /* Since the session's dead, all the stream_creation_datas should have been
+ * cancelled (which is indicated by their 'content' being NULL).
*/
- g_assert (priv->stream_creation_datas == NULL);
+ for (l = priv->stream_creation_datas; l != NULL; l = l->next)
+ {
+ StreamCreationData *d = l->data;
+ g_assert (d->content == NULL);
+ }
if (priv->delayed_request_streams != NULL)
{
@@ -853,12 +864,6 @@ gabble_media_channel_dispose (GObject *object)
priv->initial_peer = 0;
}
- if (!priv->closed)
- gabble_media_channel_close (self);
-
- g_assert (priv->closed);
- g_assert (priv->session == NULL);
-
/* All of the streams should have closed in response to the contents being
* removed when the call ended.
*/
@@ -2654,7 +2659,6 @@ static void
stream_creation_data_free (gpointer p)
{
StreamCreationData *d = p;
- GabbleMediaChannelPrivate *priv = d->self->priv;
g_free (d->name);
@@ -2664,9 +2668,15 @@ stream_creation_data_free (gpointer p)
g_object_unref (d->content);
}
- priv->stream_creation_datas = g_list_remove (priv->stream_creation_datas, d);
+ if (d->self != NULL)
+ {
+ GabbleMediaChannelPrivate *priv = d->self->priv;
+
+ g_object_remove_weak_pointer (G_OBJECT (d->self), (gpointer *) &d->self);
+ priv->stream_creation_datas = g_list_remove (
+ priv->stream_creation_datas, d);
+ }
- g_object_unref (d->self);
g_slice_free (StreamCreationData, d);
}
@@ -2675,7 +2685,7 @@ construct_stream_later_cb (gpointer user_data)
{
StreamCreationData *d = user_data;
- if (d->content != NULL)
+ if (d->content != NULL && d->self != NULL)
construct_stream (d->self, d->content, d->name, d->nat_traversal, NULL);
return FALSE;
@@ -2687,7 +2697,7 @@ google_relay_session_cb (GPtrArray *relays,
{
StreamCreationData *d = user_data;
- if (d->content != NULL)
+ if (d->content != NULL && d->self != NULL)
construct_stream (d->self, d->content, d->name, d->nat_traversal, relays);
stream_creation_data_free (d);
@@ -2697,12 +2707,14 @@ static void
content_removed_cb (GabbleJingleContent *content,
StreamCreationData *d)
{
- if (d->content != NULL)
+
+ if (d->content == NULL)
+ return;
+
+ if (d->self != NULL)
{
GList *link = d->self->priv->pending_stream_requests;
- g_signal_handler_disconnect (d->content, d->removed_id);
-
/* if any RequestStreams call was waiting for a stream to be created for
* that content, return from it unsuccessfully */
while (link != NULL)
@@ -2723,10 +2735,11 @@ content_removed_cb (GabbleJingleContent *content,
link = link->next;
}
}
-
- g_object_unref (d->content);
- d->content = NULL;
}
+
+ g_signal_handler_disconnect (d->content, d->removed_id);
+ g_object_unref (d->content);
+ d->content = NULL;
}
static void
@@ -2747,13 +2760,14 @@ create_stream_from_content (GabbleMediaChannel *self,
return;
}
-
d = g_slice_new0 (StreamCreationData);
- d->self = g_object_ref (self);
+ d->self = self;
d->name = name;
d->content = g_object_ref (c);
+ g_object_add_weak_pointer (G_OBJECT (d->self), (gpointer *) &d->self);
+
/* If the content gets removed before we've finished looking up its
* relay (can this happen?) we need to cancel the creation of the stream,
* and make any PendingStreamRequests fail */
--
1.5.6.5
More information about the telepathy-commits
mailing list