[telepathy-mission-control/master] McdDispatcher: work out the possible handlers when starting to dispatch

Simon McVittie simon.mcvittie at collabora.co.uk
Tue Apr 7 09:00:10 PDT 2009


If no handlers can take the whole batch, follow telepathy-spec by breaking
the batch into individual channels; if there are channels that have no
possible handlers, shoot them in the head rather than trying to dispatch
them (unless they're a ContactList, which is caught by a special case in
McdChannel).
---
 src/mcd-dispatch-operation.c |    4 +-
 src/mcd-dispatch-operation.h |    2 +-
 src/mcd-dispatcher.c         |  117 +++++++++++++++++++++++++++++++++++++++--
 3 files changed, 115 insertions(+), 8 deletions(-)

diff --git a/src/mcd-dispatch-operation.c b/src/mcd-dispatch-operation.c
index 469424e..87f456a 100644
--- a/src/mcd-dispatch-operation.c
+++ b/src/mcd-dispatch-operation.c
@@ -431,13 +431,15 @@ mcd_dispatch_operation_init (McdDispatchOperation *operation)
  * _mcd_dispatch_operation_new:
  * @dbus_daemon: a #TpDBusDaemon.
  * @channels: a #GList of #McdChannel elements to dispatch.
+ * @possible_handlers: the bus names of possible handlers for these channels.
  *
  * Creates a #McdDispatchOperation. The #GList @channels will be no longer
  * valid after this function has been called.
  */
 McdDispatchOperation *
 _mcd_dispatch_operation_new (TpDBusDaemon *dbus_daemon,
-                             GList *channels)
+                             GList *channels,
+                             const GStrv possible_handlers)
 {
     gpointer *obj;
     obj = g_object_new (MCD_TYPE_DISPATCH_OPERATION,
diff --git a/src/mcd-dispatch-operation.h b/src/mcd-dispatch-operation.h
index ec55fb2..0d989f9 100644
--- a/src/mcd-dispatch-operation.h
+++ b/src/mcd-dispatch-operation.h
@@ -59,7 +59,7 @@ struct _McdDispatchOperationClass
 
 GType mcd_dispatch_operation_get_type (void);
 G_GNUC_INTERNAL McdDispatchOperation *_mcd_dispatch_operation_new (
-    TpDBusDaemon *dbus_daemon, GList *channels);
+    TpDBusDaemon *dbus_daemon, GList *channels, GStrv possible_handlers);
 
 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 3b1fb9e..45e4b0d 100644
--- a/src/mcd-dispatcher.c
+++ b/src/mcd-dispatcher.c
@@ -1167,6 +1167,70 @@ handle_channels_cb (TpProxy *proxy, const GError *error, gpointer user_data,
     mcd_dispatcher_context_handler_done (context);
 }
 
+static GStrv
+mcd_dispatcher_get_possible_handlers (McdDispatcher *self,
+                                      const GList *channels)
+{
+    GList *handlers = NULL;
+    const GList *iter;
+    GHashTableIter client_iter;
+    gpointer client_p;
+    guint n_handlers = 0;
+    guint i;
+    GStrv ret;
+
+    g_hash_table_iter_init (&client_iter, self->priv->clients);
+
+    while (g_hash_table_iter_next (&client_iter, NULL, &client_p))
+    {
+        McdClient *client = client_p;
+        gboolean can_do = TRUE;
+
+        if (client->proxy == NULL ||
+            !(client->interfaces & MCD_CLIENT_HANDLER))
+        {
+            /* not a handler at all */
+            continue;
+        }
+
+        for (iter = channels; iter != NULL; iter = iter->next)
+        {
+            McdChannel *channel = MCD_CHANNEL (iter->data);
+
+            if (!match_filters (channel, client->handler_filters))
+            {
+                can_do = FALSE;
+                break;
+            }
+        }
+
+        if (can_do)
+        {
+            handlers = g_list_prepend (handlers,
+                g_strconcat (MC_CLIENT_BUS_NAME_BASE, client->name, NULL));
+            n_handlers++;
+        }
+    }
+
+    /* if no handlers can take them all, fail */
+    if (handlers == NULL)
+    {
+        return NULL;
+    }
+
+    /* we have at least one handler that can take the whole batch */
+    ret = g_new0 (gchar *, n_handlers + 1);
+
+    for (iter = handlers, i = 0; iter != NULL; iter = iter->next, i++)
+    {
+        ret[i] = iter->data;
+    }
+
+    ret[n_handlers] = NULL;
+    g_list_free (handlers);
+    return ret;
+}
+
 /*
  * mcd_dispatcher_run_handler:
  * @context: the #McdDispatcherContext.
@@ -1175,6 +1239,8 @@ handle_channels_cb (TpProxy *proxy, const GError *error, gpointer user_data,
  * This functions tries to find a handler to handle @channels, and invokes its
  * HandleChannels method. It returns a list of channels that are still
  * unhandled.
+ *
+ * FIXME: this should use mcd_dispatcher_get_possible_handlers or similar
  */
 static GList *
 mcd_dispatcher_run_handler (McdDispatcherContext *context,
@@ -1670,7 +1736,9 @@ on_operation_finished (McdDispatchOperation *operation,
 /* Entering the state machine */
 static void
 _mcd_dispatcher_enter_state_machine (McdDispatcher *dispatcher,
-				     GList *channels, gboolean requested)
+                                     GList *channels,
+                                     const GStrv possible_handlers,
+                                     gboolean requested)
 {
     McdDispatcherContext *context;
     McdDispatcherPrivate *priv;
@@ -1732,7 +1800,8 @@ _mcd_dispatcher_enter_state_machine (McdDispatcher *dispatcher,
     if (!requested)
     {
         context->operation =
-            _mcd_dispatch_operation_new (priv->dbus_daemon, channels);
+            _mcd_dispatch_operation_new (priv->dbus_daemon, channels,
+                                         possible_handlers);
 
         if (priv->operation_list_active)
         {
@@ -3197,12 +3266,48 @@ _mcd_dispatcher_take_channels (McdDispatcher *dispatcher, GList *channels,
                                gboolean requested)
 {
     GList *list;
+    GStrv possible_handlers;
 
-    for (list = channels; list != NULL; list = list->next)
-        _mcd_channel_set_status (MCD_CHANNEL (list->data),
-                                 MCD_CHANNEL_STATUS_DISPATCHING);
+    if (channels == NULL)
+    {
+        /* trivial case */
+        return;
+    }
+
+    /* See if there are any handlers that can take all these channels */
+    possible_handlers = mcd_dispatcher_get_possible_handlers (dispatcher,
+                                                              channels);
+
+    if (possible_handlers == NULL)
+    {
+        if (channels->next == NULL)
+        {
+            /* There's exactly one channel and we can't handle it. It must
+             * die. */
+            _mcd_channel_undispatchable (channels->data);
+            g_list_free (channels);
+        }
+        else
+        {
+            /* there are >= 2 channels - split the batch up and try again */
+            while (channels != NULL)
+            {
+                list = channels;
+                channels = g_list_remove_link (channels, list);
+                _mcd_dispatcher_take_channels (dispatcher, list, requested);
+            }
+        }
+    }
+    else
+    {
+        for (list = channels; list != NULL; list = list->next)
+            _mcd_channel_set_status (MCD_CHANNEL (list->data),
+                                     MCD_CHANNEL_STATUS_DISPATCHING);
 
-    _mcd_dispatcher_enter_state_machine (dispatcher, channels, requested);
+        _mcd_dispatcher_enter_state_machine (dispatcher, channels,
+                                             possible_handlers, requested);
+        g_strfreev (possible_handlers);
+    }
 }
 
 /**
-- 
1.5.6.5




More information about the telepathy-commits mailing list