[Telepathy-commits] [telepathy-mission-control/master] Implement calling of EnsureChannel

Alberto Mardegan alberto.mardegan at nokia.com
Wed Nov 19 02:27:56 PST 2008


---
 src/mcd-account-requests.c |    3 +-
 src/mcd-channel.c          |  102 ++++++++++++++++++++++++++++++++++++++++++++
 src/mcd-channel.h          |    5 ++
 src/mcd-connection.c       |   77 +++++++++++++++++++++++++++++---
 src/mcd-dispatcher.c       |   26 +++++++++++
 src/mcd-dispatcher.h       |    2 +
 6 files changed, 205 insertions(+), 10 deletions(-)

diff --git a/src/mcd-account-requests.c b/src/mcd-account-requests.c
index 4932f06..5b3bfca 100644
--- a/src/mcd-account-requests.c
+++ b/src/mcd-account-requests.c
@@ -130,14 +130,13 @@ create_request (McdAccount *account, GHashTable *properties,
     GHashTable *props;
     McdDispatcher *dispatcher;
 
-    /* TODO: handle use_existing */
-
     /* We MUST deep-copy the hash-table, as we don't know how dbus-glib will
      * free it */
     props = _mcd_deepcopy_asv (properties);
     channel = mcd_channel_new_request (props, user_time,
                                        preferred_handler);
     g_hash_table_unref (props);
+    _mcd_channel_set_request_use_existing (channel, use_existing);
 
     rd = g_slice_new (McdRequestData);
     rd->requestor_client_id = dbus_g_method_get_sender (context);
diff --git a/src/mcd-channel.c b/src/mcd-channel.c
index cbb5ff3..bd6697e 100644
--- a/src/mcd-channel.c
+++ b/src/mcd-channel.c
@@ -95,6 +95,8 @@ typedef struct
     GHashTable *properties;
     guint64 user_time;
     gchar *preferred_handler;
+
+    gboolean use_existing;
 } McdChannelRequestData;
 
 enum _McdChannelSignalType
@@ -139,6 +141,9 @@ static guint last_req_id = 1;
 
 static void _mcd_channel_release_tp_channel (McdChannel *channel,
 					     gboolean close_channel);
+static void on_proxied_channel_status_changed (McdChannel *source,
+                                               McdChannelStatus status,
+                                               McdChannel *dest);
 
 static void
 channel_request_data_free (McdChannelRequestData *crd)
@@ -1293,6 +1298,43 @@ _mcd_channel_get_request_preferred_handler (McdChannel *channel)
     return crd->preferred_handler;
 }
 
+/*
+ * _mcd_channel_set_request_use_existing:
+ * @channel: the #McdChannel.
+ * @use_existing: %TRUE if @channel must be requested via EnsureChannel.
+ *
+ * Sets the use_existing flag on @channel request.
+ */
+void
+_mcd_channel_set_request_use_existing (McdChannel *channel,
+                                       gboolean use_existing)
+{
+    McdChannelRequestData *crd;
+
+    g_return_if_fail (MCD_IS_CHANNEL (channel));
+    crd = g_object_get_data ((GObject *)channel, CD_REQUEST);
+    if (G_UNLIKELY (!crd)) return;
+    crd->use_existing = use_existing;
+}
+
+/*
+ * _mcd_channel_get_request_use_existing:
+ * @channel: the #McdChannel.
+ *
+ * Returns: %TRUE if the channel musb be requested via EnsureChannel, %FALSE
+ * otherwise.
+ */
+gboolean
+_mcd_channel_get_request_use_existing (McdChannel *channel)
+{
+    McdChannelRequestData *crd;
+
+    g_return_val_if_fail (MCD_IS_CHANNEL (channel), FALSE);
+    crd = g_object_get_data ((GObject *)channel, CD_REQUEST);
+    if (G_UNLIKELY (!crd)) return FALSE;
+    return crd->use_existing;
+}
+
 /**
  * mcd_channel_is_requested:
  * @channel: the #McdChannel.
@@ -1325,3 +1367,63 @@ mcd_channel_get_account (McdChannel *channel)
         return NULL;
 }
 
+static void
+copy_status (McdChannel *source, McdChannel *dest)
+{
+    McdChannelPrivate *src_priv, *dst_priv;
+
+    src_priv = source->priv;
+    dst_priv = dest->priv;
+    if (dst_priv->status != src_priv->status)
+    {
+        g_debug ("%s: source is %d, dest is %d", G_STRFUNC,
+                 src_priv->status, dst_priv->status);
+        if (src_priv->status == MCD_CHANNEL_FAILED)
+        {
+            const GError *error;
+
+            error = _mcd_channel_get_error (source);
+            /* this also takes care of setting the status */
+            _mcd_channel_set_error (dest, g_error_copy (error));
+        }
+        else
+            mcd_channel_set_status (dest, src_priv->status);
+    }
+
+    if (dst_priv->status == MCD_CHANNEL_FAILED ||
+        dst_priv->status == MCD_CHANNEL_DISPATCHED)
+    {
+        /* the request is completed, we are not interested in monitor the
+         * channel anymore */
+        g_signal_handlers_disconnect_by_func
+            (source, on_proxied_channel_status_changed, dest);
+        mcd_mission_abort (MCD_MISSION (dest));
+    }
+}
+
+static void
+on_proxied_channel_status_changed (McdChannel *source,
+                                   McdChannelStatus status,
+                                   McdChannel *dest)
+{
+    copy_status (source, dest);
+}
+
+/*
+ * _mcd_channel_set_request_proxy:
+ * @channel: the requested #McdChannel.
+ * @source: the #McdChannel to be proxied.
+ *
+ * This function turns @channel into a proxy for @source: it listens to
+ * "status-changed" signals from @source and replicates them on @channel
+ */
+void
+_mcd_channel_set_request_proxy (McdChannel *channel, McdChannel *source)
+{
+    g_return_if_fail (MCD_IS_CHANNEL (channel));
+    g_return_if_fail (MCD_IS_CHANNEL (source));
+    copy_status (source, channel);
+    g_signal_connect (source, "status-changed",
+                      G_CALLBACK (on_proxied_channel_status_changed), channel);
+}
+
diff --git a/src/mcd-channel.h b/src/mcd-channel.h
index bb129a2..c9421b9 100644
--- a/src/mcd-channel.h
+++ b/src/mcd-channel.h
@@ -129,6 +129,11 @@ GHashTable *_mcd_channel_get_requested_properties (McdChannel *channel);
 const gchar *_mcd_channel_get_request_path (McdChannel *channel);
 guint64 _mcd_channel_get_request_user_action_time (McdChannel *channel);
 const gchar *_mcd_channel_get_request_preferred_handler (McdChannel *channel);
+void _mcd_channel_set_request_use_existing (McdChannel *channel,
+                                            gboolean use_existing);
+gboolean _mcd_channel_get_request_use_existing (McdChannel *channel);
+
+void _mcd_channel_set_request_proxy (McdChannel *channel, McdChannel *source);
 
 void _mcd_channel_set_error (McdChannel *channel, GError *error);
 const GError *_mcd_channel_get_error (McdChannel *channel);
diff --git a/src/mcd-connection.c b/src/mcd-connection.c
index ecc29c6..a194109 100644
--- a/src/mcd-connection.c
+++ b/src/mcd-connection.c
@@ -2002,12 +2002,11 @@ request_handles_cb (TpConnection *proxy, const GArray *handles,
 }
 
 static void
-create_channel_cb (TpConnection *proxy, const gchar *channel_path,
-                   GHashTable *properties, const GError *error,
-                   gpointer user_data, GObject *weak_object)
+common_request_channel_cb (TpConnection *proxy, gboolean yours,
+                           const gchar *channel_path, GHashTable *properties,
+                           const GError *error,
+                           McdConnection *connection, McdChannel *channel)
 {
-    McdChannel *channel = MCD_CHANNEL (weak_object);
-    McdConnection *connection = user_data;
     McdConnectionPrivate *priv = connection->priv;
 
     if (error != NULL)
@@ -2025,6 +2024,38 @@ create_channel_cb (TpConnection *proxy, const gchar *channel_path,
     }
     g_debug ("%s: %p, object %s", G_STRFUNC, channel, channel_path);
 
+    /* if this was a call to EnsureChannel, it can happen that the returned
+     * channel was already created before; in that case we keep the McdChannel
+     * alive only as a proxy for the status-changed signals from the "real"
+     * McdChannel */
+    if (_mcd_channel_get_request_use_existing (channel))
+    {
+        McdChannel *existing;
+        existing = find_channel_by_path (connection, channel_path);
+        if (existing)
+        {
+            /* Two possibilities:
+             *
+             * 1) if @existing is not yet in dispatched state, proxy the
+             * signals from @existing to this request (@channel).
+             *
+             * 2) if @existing is already dispatched, we must re-invoke its
+             * handler
+             */
+            if (mcd_channel_get_status (channel) == MCD_CHANNEL_DISPATCHED)
+            {
+                g_debug ("reinvoking handler on channel %p", existing);
+                _mcd_dispatcher_reinvoke_handler (priv->dispatcher, existing);
+            }
+            else
+            {
+                g_debug ("channel %p is proxying %p", channel, existing);
+                _mcd_channel_set_request_proxy (channel, existing);
+            }
+            return;
+        }
+    }
+
     _mcd_channel_set_immutable_properties (channel,
                                            _mcd_deepcopy_asv (properties));
     /* Everything here is well and fine. We can create the channel. */
@@ -2038,6 +2069,27 @@ create_channel_cb (TpConnection *proxy, const gchar *channel_path,
      * NewChannels signal */
 }
 
+static void
+ensure_channel_cb (TpConnection *proxy, gboolean yours,
+                   const gchar *channel_path, GHashTable *properties,
+                   const GError *error,
+                   gpointer user_data, GObject *weak_object)
+{
+    common_request_channel_cb (proxy, yours, channel_path, properties, error,
+                               MCD_CONNECTION (user_data),
+                               MCD_CHANNEL (weak_object));
+}
+
+static void
+create_channel_cb (TpConnection *proxy, const gchar *channel_path,
+                   GHashTable *properties, const GError *error,
+                   gpointer user_data, GObject *weak_object)
+{
+    common_request_channel_cb (proxy, TRUE, channel_path, properties, error,
+                               MCD_CONNECTION (user_data),
+                               MCD_CHANNEL (weak_object));
+}
+
 static gboolean
 request_channel_new_iface (McdConnection *connection, McdChannel *channel)
 {
@@ -2045,9 +2097,18 @@ request_channel_new_iface (McdConnection *connection, McdChannel *channel)
     GHashTable *properties;
 
     properties = _mcd_channel_get_requested_properties (channel);
-    tp_cli_connection_interface_requests_call_create_channel
-        (priv->tp_conn, -1, properties, create_channel_cb, connection, NULL,
-         (GObject *)channel);
+    if (_mcd_channel_get_request_use_existing (channel))
+    {
+        tp_cli_connection_interface_requests_call_ensure_channel
+            (priv->tp_conn, -1, properties, ensure_channel_cb,
+             connection, NULL, (GObject *)channel);
+    }
+    else
+    {
+        tp_cli_connection_interface_requests_call_create_channel
+            (priv->tp_conn, -1, properties, create_channel_cb,
+             connection, NULL, (GObject *)channel);
+    }
     return TRUE;
 }
 
diff --git a/src/mcd-dispatcher.c b/src/mcd-dispatcher.c
index 2ca7cc2..d5aff9c 100644
--- a/src/mcd-dispatcher.c
+++ b/src/mcd-dispatcher.c
@@ -2532,3 +2532,29 @@ mcd_dispatcher_add_filters (McdDispatcher *dispatcher,
                                    filter->user_data);
 }
 
+/*
+ * _mcd_dispatcher_reinvoke_handler:
+ * @dispatcher: The #McdDispatcher.
+ * @channel: a #McdChannel.
+ *
+ * Re-invoke the channel handler for @channel.
+ */
+void
+_mcd_dispatcher_reinvoke_handler (McdDispatcher *dispatcher,
+                                  McdChannel *channel)
+{
+    McdDispatcherContext *context;
+
+    /* Preparing and filling the context */
+    context = g_new0 (McdDispatcherContext, 1);
+    context->ref_count = 1;
+    context->dispatcher = dispatcher;
+    context->channels = g_list_prepend (NULL, channel);
+
+    /* We must ref() the channel, because
+     * mcd_dispatcher_context_unref() will unref() it */
+    g_object_ref (channel);
+    mcd_dispatcher_run_handlers (context);
+    mcd_dispatcher_context_unref (context);
+}
+
diff --git a/src/mcd-dispatcher.h b/src/mcd-dispatcher.h
index 080bcd4..3e15ed3 100644
--- a/src/mcd-dispatcher.h
+++ b/src/mcd-dispatcher.h
@@ -91,6 +91,8 @@ void _mcd_dispatcher_add_request (McdDispatcher *dispatcher,
                                   McdAccount *account, McdChannel *channel);
 void _mcd_dispatcher_send_channels (McdDispatcher *dispatcher,
                                     GList *channels, gboolean requested);
+void _mcd_dispatcher_reinvoke_handler (McdDispatcher *dispatcher,
+                                       McdChannel *channel);
 
 G_END_DECLS
 
-- 
1.5.6.5




More information about the Telepathy-commits mailing list