[telepathy-mission-control/master] McdDispatcher: correctly drop refs to channels when they abort

Simon McVittie simon.mcvittie at collabora.co.uk
Tue Apr 7 14:01:39 PDT 2009


Also emit ChannelLost from McdDispatchOperation, and
make the dispatch operation close appropriately.
---
 src/mcd-dispatch-operation.c |   56 ++++++++++++++++++++++++++++++++++++++++++
 src/mcd-dispatch-operation.h |    2 +
 src/mcd-dispatcher.c         |   51 +++++++++++++++++++++++++++++++++-----
 3 files changed, 102 insertions(+), 7 deletions(-)

diff --git a/src/mcd-dispatch-operation.c b/src/mcd-dispatch-operation.c
index a2232c2..94e66eb 100644
--- a/src/mcd-dispatch-operation.c
+++ b/src/mcd-dispatch-operation.c
@@ -37,6 +37,7 @@
 #include <telepathy-glib/util.h>
 
 #include "mcd-dbusprop.h"
+#include "mcd-misc.h"
 #include "_gen/interfaces.h"
 #include "_gen/gtypes.h"
 
@@ -589,3 +590,58 @@ mcd_dispatch_operation_handle_with (McdDispatchOperation *operation,
     mcd_dispatch_operation_finish (operation);
 }
 
+void
+_mcd_dispatch_operation_lose_channel (McdDispatchOperation *self,
+                                      McdChannel *channel,
+                                      GList **channels)
+{
+    GList *li = g_list_find (self->priv->channels, channel);
+    const gchar *object_path;
+
+    if (li == NULL)
+    {
+        return;
+    }
+
+    self->priv->channels = g_list_delete_link (self->priv->channels, li);
+
+    /* Because the McdDispatcherContext has a borrowed copy of our list
+     * of channels, we need to tell it the new head of the list, in case
+     * we've just removed the first link. Further, we need to do this before
+     * emitting any signals.
+     *
+     * This is amazingly fragile. */
+    *channels = self->priv->channels;
+
+    object_path = mcd_channel_get_object_path (channel);
+
+    if (object_path == NULL)
+    {
+        /* This shouldn't happen, but McdChannel is twisty enough that I
+         * can't be sure */
+        g_critical ("McdChannel has already lost its TpChannel: %p",
+            channel);
+    }
+    else
+    {
+        const GError *error = mcd_channel_get_error (channel);
+        gchar *error_name = _mcd_build_error_string (error);
+
+        mc_svc_channel_dispatch_operation_emit_channel_lost (self, object_path,
+                                                             error_name,
+                                                             error->message);
+        g_free (error_name);
+    }
+
+    /* We previously had a ref in the linked list - drop it */
+    g_object_unref (channel);
+
+    if (self->priv->channels == NULL)
+    {
+        /* no channels left, so the CDO finishes */
+        if (!self->priv->finished)
+        {
+            mcd_dispatch_operation_finish (self);
+        }
+    }
+}
diff --git a/src/mcd-dispatch-operation.h b/src/mcd-dispatch-operation.h
index 0d989f9..d09fb70 100644
--- a/src/mcd-dispatch-operation.h
+++ b/src/mcd-dispatch-operation.h
@@ -60,6 +60,8 @@ struct _McdDispatchOperationClass
 GType mcd_dispatch_operation_get_type (void);
 G_GNUC_INTERNAL McdDispatchOperation *_mcd_dispatch_operation_new (
     TpDBusDaemon *dbus_daemon, GList *channels, GStrv possible_handlers);
+G_GNUC_INTERNAL void _mcd_dispatch_operation_lose_channel (
+    McdDispatchOperation *self, McdChannel *channel, GList **channels);
 
 const gchar *mcd_dispatch_operation_get_path (McdDispatchOperation *operation);
 GHashTable *mcd_dispatch_operation_get_properties
diff --git a/src/mcd-dispatcher.c b/src/mcd-dispatcher.c
index df682a5..d75695d 100644
--- a/src/mcd-dispatcher.c
+++ b/src/mcd-dispatcher.c
@@ -1680,18 +1680,50 @@ static void
 on_channel_abort_context (McdChannel *channel, McdDispatcherContext *context)
 {
     const GError *error;
-    DEBUG ("Channel %p aborted while in a dispatcher context", channel);
+    GList *li = g_list_find (context->channels, channel);
 
-    /* TODO: it's still not clear what we should do with these aborted
-     * channels; for now, we keep them in the context, pretending that nothing
-     * happened -- the channel handler will see that they don't exist anymore
-     */
+    DEBUG ("Channel %p aborted while in a dispatcher context", channel);
 
-    /* but if it was a channel request, and it was cancelled, then the whole
+    /* if it was a channel request, and it was cancelled, then the whole
      * context should be aborted */
     error = mcd_channel_get_error (channel);
     if (error && error->code == TP_ERROR_CANCELLED)
         context->cancelled = TRUE;
+
+    /* Losing the channel might mean we get freed, which would make some of
+     * the operations below very unhappy */
+    mcd_dispatcher_context_ref (context);
+
+    if (context->operation)
+    {
+        /* the CDO owns the linked list and we just borrow it; in case it's
+         * the head of the list that we're deleting, we need to ask the CDO
+         * to update our idea of what the list is before emitting any signals.
+         *
+         * FIXME: this is alarmingly fragile */
+        _mcd_dispatch_operation_lose_channel (context->operation, channel,
+                                              &(context->channels));
+
+        if (li != NULL)
+        {
+            /* we used to have a ref to it, until the CDO removed it from the
+             * linked list. (Do not dereference li at this point - it has
+             * been freed!) */
+            g_object_unref (channel);
+        }
+    }
+    else
+    {
+        /* we own the linked list */
+        context->channels = g_list_delete_link (context->channels, li);
+    }
+
+    if (context->channels == NULL)
+    {
+        DEBUG ("Nothing left in this context");
+    }
+
+    mcd_dispatcher_context_unref (context);
 }
 
 static void
@@ -1708,7 +1740,12 @@ on_operation_finished (McdDispatchOperation *operation,
             context->dispatcher, mcd_dispatch_operation_get_path (operation));
     }
 
-    if (mcd_dispatch_operation_is_claimed (operation))
+    if (context->channels == NULL)
+    {
+        DEBUG ("Nothing left to dispatch");
+        mcd_dispatcher_context_handler_done (context);
+    }
+    else if (mcd_dispatch_operation_is_claimed (operation))
     {
         GList *list;
 
-- 
1.5.6.5



More information about the telepathy-commits mailing list