[telepathy-gabble/master] Ensure MediaChannel:streams is always non-NULL
Will Thompson
will.thompson at collabora.co.uk
Mon May 11 09:47:35 PDT 2009
This simplifies reasoning about the class, and fixes a bug where calling
SessionHandler.Ready() could crash: it iterated priv->streams without
checking if it's NULL.
---
src/media-channel-internal.h | 2 +-
src/media-channel.c | 77 +++++++++++++++++++++++++-----------------
2 files changed, 47 insertions(+), 32 deletions(-)
diff --git a/src/media-channel-internal.h b/src/media-channel-internal.h
index c517706..6138f66 100644
--- a/src/media-channel-internal.h
+++ b/src/media-channel-internal.h
@@ -42,7 +42,7 @@ struct _GabbleMediaChannelPrivate
GabbleJingleSession *session;
- /* array of referenced GabbleMediaStream* */
+ /* array of referenced GabbleMediaStream*. Always non-NULL. */
GPtrArray *streams;
/* list of PendingStreamRequest* in no particular order */
GList *pending_stream_requests;
diff --git a/src/media-channel.c b/src/media-channel.c
index de6d7f8..cc00bd8 100644
--- a/src/media-channel.c
+++ b/src/media-channel.c
@@ -163,6 +163,7 @@ gabble_media_channel_init (GabbleMediaChannel *self)
priv->next_stream_id = 1;
priv->delayed_request_streams = g_ptr_array_sized_new (1);
+ priv->streams = g_ptr_array_sized_new (1);
/* initialize properties mixin */
tp_properties_mixin_init (G_OBJECT (self), G_STRUCT_OFFSET (
@@ -243,9 +244,7 @@ _latch_to_session (GabbleMediaChannel *chan)
g_signal_connect (priv->session, "terminated",
(GCallback) session_terminated_cb, chan);
- g_assert (priv->streams == NULL);
-
- priv->streams = g_ptr_array_sized_new (1);
+ g_assert (priv->streams->len == 0);
tp_svc_channel_interface_media_signalling_emit_new_session_handler (
G_OBJECT (chan), priv->object_path, "rtp");
@@ -843,7 +842,13 @@ gabble_media_channel_dispose (GObject *object)
g_assert (priv->closed);
g_assert (priv->session == NULL);
- g_assert (priv->streams == NULL);
+
+ /* All of the streams should have closed in response to the contents being
+ * removed when the call ended.
+ */
+ g_assert (priv->streams->len == 0);
+ g_ptr_array_free (priv->streams, TRUE);
+ priv->streams = NULL;
if (G_OBJECT_CLASS (gabble_media_channel_parent_class)->dispose)
G_OBJECT_CLASS (gabble_media_channel_parent_class)->dispose (object);
@@ -2187,6 +2192,26 @@ gabble_media_channel_remove_member (GObject *obj,
return TRUE;
}
+/**
+ * copy_stream_list:
+ *
+ * Returns a copy of priv->streams. This is used when applying a function to
+ * all streams that could result in them being closed, to avoid stream_close_cb
+ * modifying the list being iterated.
+ */
+static GPtrArray *
+copy_stream_list (GabbleMediaChannel *channel)
+{
+ GabbleMediaChannelPrivate *priv = channel->priv;
+ guint i;
+ GPtrArray *ret = g_ptr_array_sized_new (priv->streams->len);
+
+ for (i = 0; i < priv->streams->len; i++)
+ g_ptr_array_add (ret, g_ptr_array_index (priv->streams, i));
+
+ return ret;
+}
+
static void
session_terminated_cb (GabbleJingleSession *session,
gboolean local_terminator,
@@ -2234,19 +2259,16 @@ session_terminated_cb (GabbleJingleSession *session,
g_list_free (priv->pending_stream_requests);
priv->pending_stream_requests = NULL;
- /* unref streams */
- if (priv->streams != NULL)
- {
- GPtrArray *tmp = priv->streams;
+ {
+ GPtrArray *tmp = copy_stream_list (channel);
- DEBUG ("unreffing streams");
+ g_ptr_array_foreach (tmp, (GFunc) gabble_media_stream_close, NULL);
- /* move priv->streams aside so that the stream_close_cb
- * doesn't double unref */
- priv->streams = NULL;
- g_ptr_array_foreach (tmp, (GFunc) g_object_unref, NULL);
- g_ptr_array_free (tmp, TRUE);
- }
+ /* All the streams should have closed. */
+ g_assert (priv->streams->len == 0);
+
+ g_ptr_array_free (tmp, TRUE);
+ }
/* remove the session */
g_object_unref (priv->session);
@@ -2327,14 +2349,13 @@ stream_close_cb (GabbleMediaStream *stream,
tp_svc_channel_type_streamed_media_emit_stream_removed (chan, id);
- if (priv->streams != NULL)
- {
- g_ptr_array_remove (priv->streams, stream);
-
- gabble_media_channel_hold_stream_closed (chan, stream);
+ if (g_ptr_array_remove (priv->streams, stream))
+ g_object_unref (stream);
+ else
+ g_warning ("stream %p (%s) removed, but it wasn't in priv->streams!",
+ stream, stream->name);
- g_object_unref (stream);
- }
+ gabble_media_channel_hold_stream_closed (chan, stream);
}
static void
@@ -2800,24 +2821,18 @@ gabble_media_channel_error (TpSvcMediaSessionHandler *iface,
return;
}
- g_assert (priv->streams != NULL);
-
/* Calling gabble_media_stream_error () on all the streams will ultimately
* cause them all to emit 'closed'. In response to 'closed', stream_close_cb
- * normally unrefs them, and removes them from priv->streams. We're iterating
- * across priv->streams here, so we don't want it to be modified from
- * underneath us. So, we move it aside, and unref each stream here.
+ * unrefs them, and removes them from priv->streams. So, we copy the stream
+ * list to avoid it being modified from underneath us.
*/
- tmp = priv->streams;
- priv->streams = NULL;
+ tmp = copy_stream_list (self);
for (i = 0; i < tmp->len; i++)
{
GabbleMediaStream *stream = g_ptr_array_index (tmp, i);
gabble_media_stream_error (stream, errno, message, NULL);
-
- g_object_unref (stream);
}
g_ptr_array_free (tmp, TRUE);
--
1.5.6.5
More information about the telepathy-commits
mailing list