[telepathy-mission-control/master] McdDispatcher: refactor how plugins close channels

Simon McVittie simon.mcvittie at collabora.co.uk
Tue Jun 30 10:34:13 PDT 2009


mcd_dispatcher_context_process has two purposes: it releases the filter's
implicit reference to the context/lock on the context, and it can
arrange for channels to be terminated.

It seems cleaner to introduce mcd_dispatcher_context_proceed() which
just releases the reference/lock at all times, and redefine
mcd_dispatcher_context_process (which will be deprecated later) in terms
of it.

To ensure that proceeding after channels have been "dealt with" does not
invoke clients, new function mcd_dispatcher_context_forget_all provides
a way to tell the context that its channels are no longer suitable for
dispatching, removing them (as a result of the abort signal). proceed
needs to handle this by just clearing up if all the channels have already
gone.

The remaining bit of new API needed is an easy way to terminate channels,
possibly with a reason or with Destroy. mcd_dispatcher_context_close_all
and mcd_dispatcher_context_destroy_all provide this.
---
 src/mcd-dispatcher-context.h |   10 ++
 src/mcd-dispatcher.c         |  209 ++++++++++++++++++++++++++++++++++--------
 2 files changed, 180 insertions(+), 39 deletions(-)

diff --git a/src/mcd-dispatcher-context.h b/src/mcd-dispatcher-context.h
index bf00c21..a0fc9c6 100644
--- a/src/mcd-dispatcher-context.h
+++ b/src/mcd-dispatcher-context.h
@@ -89,6 +89,14 @@ McdChannel *mcd_dispatcher_context_get_channel_by_type
 McdConnection *mcd_dispatcher_context_get_connection
     (McdDispatcherContext *context);
 
+void mcd_dispatcher_context_close_all (McdDispatcherContext *context,
+                                       TpChannelGroupChangeReason reason,
+                                       const gchar *message);
+
+void mcd_dispatcher_context_destroy_all (McdDispatcherContext *context);
+
+void mcd_dispatcher_context_forget_all (McdDispatcherContext *context);
+
 /* Statemachine API section */
 
 /* Will step through the state machine.
@@ -96,6 +104,8 @@ McdConnection *mcd_dispatcher_context_get_connection
  * @param result: The return code
  */
 
+void mcd_dispatcher_context_proceed (McdDispatcherContext *context);
+
 void mcd_dispatcher_context_process (McdDispatcherContext * ctx, gboolean result);
 
 const gchar *mcd_dispatcher_context_get_protocol_name (McdDispatcherContext *) G_GNUC_DEPRECATED;
diff --git a/src/mcd-dispatcher.c b/src/mcd-dispatcher.c
index d4574eb..fa7cf43 100644
--- a/src/mcd-dispatcher.c
+++ b/src/mcd-dispatcher.c
@@ -2842,54 +2842,185 @@ mcd_dispatcher_new (TpDBusDaemon *dbus_daemon, McdMaster *master)
     return obj;
 }
 
-/* The new state machine walker function for pluginized filters*/
-
+/**
+ * mcd_dispatcher_context_proceed:
+ * @context: a #McdDispatcherContext
+ *
+ * Must be called by plugin filters exactly once per invocation of the filter
+ * function, to proceed with processing of the @context. This does nothing
+ * if @context has already finished.
+ */
 void
-mcd_dispatcher_context_process (McdDispatcherContext * context, gboolean result)
+mcd_dispatcher_context_proceed (McdDispatcherContext *context)
 {
-    if (result && !context->cancelled)
-    {
-	McdFilter *filter;
-
-	filter = g_list_nth_data (context->chain, context->next_func_index);
-	/* Do we still have functions to go through? */
-	if (filter)
-	{
-	    context->next_func_index++;
-	    
-            DEBUG ("Next filter");
-            mcd_dispatcher_context_ref (context, "CTXREF10");
-	    filter->func (context, filter->user_data);
-            mcd_dispatcher_context_unref (context, "CTXREF10");
-	    return; /*State machine goes on...*/
-	}
-	else
-	{
-	    mcd_dispatcher_run_clients (context);
-	}
+    GError error = { TP_ERRORS, 0, NULL };
+    McdFilter *filter;
+
+    if (context->cancelled)
+    {
+        error.code = TP_ERROR_CANCELLED;
+        error.message = "Channel request cancelled";
+        _mcd_dispatcher_context_abort (context, &error);
+        goto no_more;
     }
-    else
+
+    if (context->channels == NULL)
     {
-        GError error;
+        DEBUG ("No channels left");
+        goto no_more;
+    }
 
-        if (context->cancelled)
-        {
-            error.domain = TP_ERRORS;
-            error.code = TP_ERROR_CANCELLED;
-            error.message = "Context cancelled";
-        }
-        else
-        {
-            DEBUG ("Filters failed, disposing request");
-            error.domain = TP_ERRORS;
-            error.code = TP_ERROR_NOT_AVAILABLE;
-            error.message = "Filters failed";
-        }
-        _mcd_dispatcher_context_abort (context, &error);
+    filter = g_list_nth_data (context->chain, context->next_func_index);
+
+    if (filter != NULL)
+    {
+        context->next_func_index++;
+        DEBUG ("Next filter");
+        mcd_dispatcher_context_ref (context, "CTXREF10");
+        filter->func (context, filter->user_data);
+        mcd_dispatcher_context_unref (context, "CTXREF10");
+        /* The state machine goes on... this function will be invoked again
+         * (perhaps recursively, or perhaps later) by filter->func. */
+        return;
     }
+
+    mcd_dispatcher_run_clients (context);
+
+no_more:
     mcd_dispatcher_context_unref (context, "CTXREF01");
 }
 
+/**
+ * mcd_dispatcher_context_forget_all:
+ * @context: a #McdDispatcherContext
+ *
+ * Stop processing channels in @context, but do not close them. They will
+ * no longer be dispatched, and the ChannelDispatchOperation (if any)
+ * will emit ChannelLost.
+ */
+void
+mcd_dispatcher_context_forget_all (McdDispatcherContext *context)
+{
+    GList *list;
+
+    g_return_if_fail (context);
+
+    /* make a temporary copy, which is destroyed during the loop - otherwise
+     * we'll be trying to iterate over context->channels at the same time
+     * that mcd_mission_abort results in modifying it, which would be bad */
+    list = g_list_copy (context->channels);
+    g_list_foreach (list, (GFunc) g_object_ref, NULL);
+
+    while (list != NULL)
+    {
+        mcd_mission_abort (list->data);
+        g_object_unref (list->data);
+        list = g_list_delete_link (list, list);
+    }
+
+    g_return_if_fail (context->channels == NULL);
+}
+
+/**
+ * mcd_dispatcher_context_destroy_all:
+ * @context: a #McdDispatcherContext
+ *
+ * Consider all channels in the #McdDispatcherContext to be undispatchable,
+ * and close them destructively. Information loss might result.
+ *
+ * Plugins must still call mcd_dispatcher_context_proceed() afterwards,
+ * to release their reference to the dispatcher context.
+ */
+void
+mcd_dispatcher_context_destroy_all (McdDispatcherContext *context)
+{
+    GList *list;
+
+    g_return_if_fail (context);
+
+    list = g_list_copy (context->channels);
+    g_list_foreach (list, (GFunc) g_object_ref, NULL);
+
+    while (list != NULL)
+    {
+        _mcd_channel_undispatchable (list->data);
+        g_object_unref (list->data);
+        list = g_list_delete_link (list, list);
+    }
+
+    mcd_dispatcher_context_forget_all (context);
+}
+
+/**
+ * mcd_dispatcher_context_close_all:
+ * @context: a #McdDispatcherContext
+ * @reason: a reason code
+ * @message: a message to be used if applicable, which should be "" if
+ *  no message is appropriate
+ *
+ * Close all channels in the #McdDispatcherContext. If @reason is not
+ * %TP_CHANNEL_GROUP_CHANGE_REASON_NONE and/or @message is non-empty,
+ * attempt to use the RemoveMembersWithReason D-Bus method to specify
+ * a message and reason, falling back to the Close method if that doesn't
+ * work.
+ *
+ * Plugins must still call mcd_dispatcher_context_proceed() afterwards,
+ * to release their reference to the dispatcher context.
+ */
+void
+mcd_dispatcher_context_close_all (McdDispatcherContext *context,
+                                  TpChannelGroupChangeReason reason,
+                                  const gchar *message)
+{
+    GList *list;
+
+    g_return_if_fail (context);
+
+    if (message == NULL)
+    {
+        message = "";
+    }
+
+    list = g_list_copy (context->channels);
+    g_list_foreach (list, (GFunc) g_object_ref, NULL);
+
+    while (list != NULL)
+    {
+        _mcd_channel_depart (list->data, reason, message);
+        g_object_unref (list->data);
+        list = g_list_delete_link (list, list);
+    }
+
+    mcd_dispatcher_context_forget_all (context);
+}
+
+/**
+ * mcd_dispatcher_context_process:
+ * @context: a #McdDispatcherContext
+ * @result: %FALSE if the channels are to be destroyed
+ *
+ * Continue to process the @context.
+ *
+ * mcd_dispatcher_context_process (c, TRUE) is exactly equivalent to
+ * mcd_dispatcher_context_proceed (c), which should be used instead in new
+ * code.
+ *
+ * mcd_dispatcher_context_process (c, TRUE) is exactly equivalent to
+ * mcd_dispatcher_context_destroy_all (c) followed by
+ * mcd_dispatcher_context_proceed (c), which should be used instead in new
+ * code.
+ */
+void
+mcd_dispatcher_context_process (McdDispatcherContext * context, gboolean result)
+{
+    if (!result)
+    {
+        mcd_dispatcher_context_destroy_all (context);
+    }
+
+    mcd_dispatcher_context_proceed (context);
+}
+
 static void
 mcd_dispatcher_context_unref (McdDispatcherContext * context,
                               const gchar *tag)
-- 
1.5.6.5




More information about the telepathy-commits mailing list