[glib/gdbus: 6/6] Add a method on GDBusConnection to send D-Bus messages



commit 14c084f8eb9d8905c2ee8de7ca0d9af8d483156f
Author: David Zeuthen <davidz redhat com>
Date:   Wed Apr 22 11:38:29 2009 -0400

    Add a method on GDBusConnection to send D-Bus messages
    
    It's tricky getting cancellation right etc. and right now we have a
    lot of duplicated code for this.
---
 docs/reference/gdbus/gdbus-sections.txt |    3 +
 gdbus/gdbus.symbols                     |    5 +-
 gdbus/gdbusconnection.c                 |  223 +++++++++++++++++++++++++++++++
 gdbus/gdbusconnection.h                 |   63 +++++----
 gdbus/tests/connection.c                |  148 ++++++++++++++++++++
 5 files changed, 415 insertions(+), 27 deletions(-)

diff --git a/docs/reference/gdbus/gdbus-sections.txt b/docs/reference/gdbus/gdbus-sections.txt
index d4dd5a1..103a4f8 100644
--- a/docs/reference/gdbus/gdbus-sections.txt
+++ b/docs/reference/gdbus/gdbus-sections.txt
@@ -62,6 +62,9 @@ g_dbus_connection_get_is_open
 g_dbus_connection_get_is_opening
 g_dbus_connection_get_bus_type
 g_dbus_connection_get_dbus_1_connection
+g_dbus_connection_send_dbus_1_message
+g_dbus_connection_send_dbus_1_message_with_reply
+g_dbus_connection_send_dbus_1_message_with_reply_finish
 <SUBSECTION Standard>
 G_DBUS_CONNECTION
 G_IS_DBUS_CONNECTION
diff --git a/gdbus/gdbus.symbols b/gdbus/gdbus.symbols
index 7d3ef6e..c6b6045 100644
--- a/gdbus/gdbus.symbols
+++ b/gdbus/gdbus.symbols
@@ -24,9 +24,12 @@ g_dbus_connection_get_unique_name
 g_dbus_connection_get_is_open
 g_dbus_connection_get_is_opening
 g_dbus_connection_get_bus_type
-g_dbus_connection_get_dbus_1_connection
 g_dbus_connection_get_exit_on_close
 g_dbus_connection_set_exit_on_close
+g_dbus_connection_get_dbus_1_connection
+g_dbus_connection_send_dbus_1_message
+g_dbus_connection_send_dbus_1_message_with_reply
+g_dbus_connection_send_dbus_1_message_with_reply_finish
 #endif
 #endif
 
diff --git a/gdbus/gdbusconnection.c b/gdbus/gdbusconnection.c
index 24edb83..8a3bcb7 100644
--- a/gdbus/gdbusconnection.c
+++ b/gdbus/gdbusconnection.c
@@ -1068,6 +1068,229 @@ g_dbus_connection_get_unique_name (GDBusConnection *connection)
     return NULL;
 }
 
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * g_dbus_connection_send_dbus_1_message:
+ * @connection: A #GDBusConnection.
+ * @message: A #DBusMessage
+ *
+ * Sends @message on @connection. If @connection is closed, this function is a no-op.
+ *
+ * This function is intended for use by object mappings only.
+ **/
+void
+g_dbus_connection_send_dbus_1_message (GDBusConnection    *connection,
+                                       DBusMessage        *message)
+{
+  /* TODO: implement */
+  g_assert_not_reached ();
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+send_dbus_1_message_with_reply_cb (DBusPendingCall *pending_call,
+                                   void            *user_data)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+  GCancellable *cancellable;
+  gulong cancellable_handler_id;
+  DBusMessage *reply;
+
+  G_LOCK (connection_lock);
+  cancellable_handler_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (simple), "cancellable-handler-id"));
+  G_UNLOCK (connection_lock);
+
+  cancellable = g_object_get_data (G_OBJECT (simple), "cancellable");
+
+  if (cancellable_handler_id > 0)
+    g_cancellable_disconnect (cancellable, cancellable_handler_id);
+
+  if (pending_call == NULL)
+    {
+      g_simple_async_result_set_error (simple,
+                                       G_DBUS_ERROR,
+                                       G_DBUS_ERROR_CANCELLED,
+                                       _("Operation was cancelled"));
+    }
+  else
+    {
+      reply = dbus_pending_call_steal_reply (pending_call);
+      g_assert (reply != NULL);
+      g_simple_async_result_set_op_res_gpointer (simple, reply, (GDestroyNotify) dbus_message_unref);
+    }
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+static gboolean
+send_dbus_1_message_with_reply_cancelled_in_idle (gpointer user_data)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+  send_dbus_1_message_with_reply_cb (NULL, simple);
+  return FALSE;
+}
+
+static void
+send_dbus_1_message_with_reply_cancelled_cb (GCancellable *cancellable,
+                                             gpointer      user_data)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+  DBusPendingCall *pending_call;
+
+  pending_call = g_object_get_data (G_OBJECT (simple), "dbus-1-pending-call");
+  dbus_pending_call_cancel (pending_call);
+
+  g_idle_add (send_dbus_1_message_with_reply_cancelled_in_idle, simple);
+}
+
+/**
+ * g_dbus_connection_send_dbus_1_message_with_reply:
+ * @connection: A #GDBusConnection.
+ * @message: A #DBusMessage
+ * @timeout_msec: The timeout in milliseconds or -1 to use the default timeout.
+ * @cancellable: A #GCancellable or %NULL.
+ * @callback: Callback function to invoke when the reply is ready.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously sends @message on @connection and invokes @callback when the reply is
+ * ready. In @callback you should call g_dbus_connection_send_dbus_1_message_with_reply_finish() to
+ * get the reply.
+ *
+ * Note that it is considered a programming error if @message is not a
+ * method-call message.
+ *
+ * It is not considered a programming error to use this function if @connection is
+ * closed - sending the message will fail with %G_DBUS_ERROR_FAILED.
+ *
+ * This function is intended for use by object mappings only.
+ **/
+void
+g_dbus_connection_send_dbus_1_message_with_reply (GDBusConnection    *connection,
+                                                  DBusMessage        *message,
+                                                  gint                timeout_msec,
+                                                  GCancellable       *cancellable,
+                                                  GAsyncReadyCallback callback,
+                                                  gpointer            user_data)
+{
+  GSimpleAsyncResult *simple;
+  DBusPendingCall *pending_call;
+  gulong cancellable_handler_id;
+
+  g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
+  g_return_if_fail (callback != NULL);
+  g_return_if_fail (message != NULL);
+  g_return_if_fail (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_CALL);
+
+  simple = g_simple_async_result_new (G_OBJECT (connection),
+                                      callback,
+                                      user_data,
+                                      g_dbus_connection_send_dbus_1_message_with_reply);
+
+  /* don't even send a message if already cancelled */
+  if (g_cancellable_is_cancelled (cancellable))
+    {
+      g_simple_async_result_set_error (simple,
+                                       G_DBUS_ERROR,
+                                       G_DBUS_ERROR_CANCELLED,
+                                       _("Operation was cancelled"));
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+      goto out;
+    }
+
+  if (connection->priv->dbus_1_connection == NULL)
+    {
+      g_simple_async_result_set_error (simple,
+                                       G_DBUS_ERROR,
+                                       G_DBUS_ERROR_FAILED,
+                                       _("Not connected"));
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+      goto out;
+    }
+
+  if (!dbus_connection_send_with_reply (connection->priv->dbus_1_connection,
+                                        message,
+                                        &pending_call,
+                                        timeout_msec))
+    _g_dbus_oom ();
+
+  g_object_set_data_full (G_OBJECT (simple),
+                          "dbus-1-pending-call",
+                          pending_call,
+                          (GDestroyNotify) dbus_pending_call_unref);
+
+  g_object_set_data (G_OBJECT (simple),
+                     "cancellable",
+                     cancellable);
+
+  dbus_pending_call_set_notify (pending_call,
+                                send_dbus_1_message_with_reply_cb,
+                                simple,
+                                NULL);
+
+  cancellable_handler_id = 0;
+  if (cancellable != NULL)
+    {
+      /* use the lock to ensure cancellable-handler-id is set on simple before trying to get it
+       * in send_dbus_1_message_with_reply_cb()
+       */
+      G_LOCK (connection_lock);
+      cancellable_handler_id = g_cancellable_connect (cancellable,
+                                                      G_CALLBACK (send_dbus_1_message_with_reply_cancelled_cb),
+                                                      simple,
+                                                      NULL);
+      g_object_set_data (G_OBJECT (simple),
+                         "cancellable-handler-id",
+                         GUINT_TO_POINTER (cancellable_handler_id));
+      G_UNLOCK (connection_lock);
+    }
+
+ out:
+  ;
+}
+
+/**
+ * g_dbus_connection_send_dbus_1_message_with_reply_finish:
+ * @connection: A #GDBusConnection.
+ * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback function passed
+ * to g_dbus_connection_send_dbus_1_message_with_reply().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes sending a message with reply.
+ *
+ * Note that @error is only set if the #GCancellable passed to g_dbus_connection_send_dbus_1_message_with_reply()
+ * was cancelled (in which case %G_DBUS_ERROR_CANCELLED is returned) or if @connection was not open (in
+ * which case %G_DBUS_ERROR_FAILED is returned). Specifically, the returned #DBusMessage message can
+ * be an error message (cf. dbus_message_is_error()). If your object mapping uses #GError you can use
+ * the utility function g_dbus_error_new_for_dbus_error() to map this to a #GError.
+ *
+ * This function is intended for use by object mappings only.
+ *
+ * Returns: A #DBusMessage or %NULL if @error is set. Free with dbus_message_unref().
+ **/
+DBusMessage *
+g_dbus_connection_send_dbus_1_message_with_reply_finish (GDBusConnection   *connection,
+                                                         GAsyncResult      *res,
+                                                         GError           **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+  DBusMessage *reply;
+
+  g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_dbus_connection_send_dbus_1_message_with_reply);
+
+  reply = NULL;
+  if (g_simple_async_result_propagate_error (simple, error))
+    goto out;
+
+  reply = dbus_message_ref (g_simple_async_result_get_op_res_gpointer (simple));
+
+ out:
+  return reply;
+}
+
 
 #define __G_DBUS_CONNECTION_C__
 #include "gdbusaliasdef.c"
diff --git a/gdbus/gdbusconnection.h b/gdbus/gdbusconnection.h
index 6f00b2a..87e0f4d 100644
--- a/gdbus/gdbusconnection.h
+++ b/gdbus/gdbusconnection.h
@@ -85,32 +85,43 @@ struct _GDBusConnectionClass
   void (*_g_reserved8) (void);
 };
 
-GType            g_dbus_connection_get_type               (void) G_GNUC_CONST;
-
-GDBusConnection *g_dbus_connection_bus_get                (GBusType             bus_type,
-                                                           GCancellable        *cancellable,
-                                                           GAsyncReadyCallback  callback,
-                                                           gpointer             user_data);
-gboolean         g_dbus_connection_bus_get_finish         (GDBusConnection     *connection,
-                                                           GAsyncResult        *res,
-                                                           GError             **error);
-
-GDBusConnection *g_dbus_connection_bus_get_private        (GBusType             bus_type,
-                                                           GCancellable        *cancellable,
-                                                           GAsyncReadyCallback  callback,
-                                                           gpointer             user_data);
-gboolean         g_dbus_connection_bus_get_private_finish (GDBusConnection     *connection,
-                                                           GAsyncResult        *res,
-                                                           GError             **error);
-
-GBusType         g_dbus_connection_get_bus_type           (GDBusConnection *connection);
-gboolean         g_dbus_connection_get_is_open            (GDBusConnection *connection);
-gboolean         g_dbus_connection_get_is_opening         (GDBusConnection *connection);
-DBusConnection  *g_dbus_connection_get_dbus_1_connection  (GDBusConnection *connection);
-const gchar     *g_dbus_connection_get_unique_name        (GDBusConnection *connection);
-gboolean         g_dbus_connection_get_exit_on_close      (GDBusConnection *connection);
-void             g_dbus_connection_set_exit_on_close      (GDBusConnection *connection,
-                                                           gboolean         exit_on_close);
+GType            g_dbus_connection_get_type                   (void) G_GNUC_CONST;
+GDBusConnection *g_dbus_connection_bus_get                    (GBusType               bus_type,
+                                                               GCancellable          *cancellable,
+                                                               GAsyncReadyCallback    callback,
+                                                               gpointer               user_data);
+gboolean         g_dbus_connection_bus_get_finish             (GDBusConnection       *connection,
+                                                               GAsyncResult          *res,
+                                                               GError               **error);
+GDBusConnection *g_dbus_connection_bus_get_private            (GBusType               bus_type,
+                                                               GCancellable          *cancellable,
+                                                               GAsyncReadyCallback    callback,
+                                                               gpointer               user_data);
+gboolean         g_dbus_connection_bus_get_private_finish     (GDBusConnection       *connection,
+                                                               GAsyncResult          *res,
+                                                               GError               **error);
+GBusType         g_dbus_connection_get_bus_type               (GDBusConnection    *connection);
+gboolean         g_dbus_connection_get_is_open                (GDBusConnection    *connection);
+gboolean         g_dbus_connection_get_is_opening             (GDBusConnection    *connection);
+const gchar     *g_dbus_connection_get_unique_name            (GDBusConnection    *connection);
+gboolean         g_dbus_connection_get_exit_on_close          (GDBusConnection    *connection);
+void             g_dbus_connection_set_exit_on_close          (GDBusConnection    *connection,
+                                                               gboolean            exit_on_close);
+
+/* the following functions are intended only for language bindings and object mappings */
+
+DBusConnection  *g_dbus_connection_get_dbus_1_connection                 (GDBusConnection    *connection);
+void             g_dbus_connection_send_dbus_1_message                   (GDBusConnection    *connection,
+                                                                          DBusMessage        *message);
+void             g_dbus_connection_send_dbus_1_message_with_reply        (GDBusConnection    *connection,
+                                                                          DBusMessage        *message,
+                                                                          gint                timeout_msec,
+                                                                          GCancellable       *cancellable,
+                                                                          GAsyncReadyCallback callback,
+                                                                          gpointer            user_data);
+DBusMessage     *g_dbus_connection_send_dbus_1_message_with_reply_finish (GDBusConnection    *connection,
+                                                                          GAsyncResult       *res,
+                                                                          GError            **error);
 
 G_END_DECLS
 
diff --git a/gdbus/tests/connection.c b/gdbus/tests/connection.c
index 5628429..c4840c0 100644
--- a/gdbus/tests/connection.c
+++ b/gdbus/tests/connection.c
@@ -626,6 +626,153 @@ test_message_bus_connections (void)
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
+/* Test that g_dbus_connection_send() works */
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+msg_cb_expect_error (GDBusConnection *connection,
+                     GAsyncResult    *res,
+                     gpointer         user_data)
+{
+  GError *error;
+  DBusMessage *reply;
+
+  error = NULL;
+  reply = g_dbus_connection_send_dbus_1_message_with_reply_finish (connection,
+                                                                   res,
+                                                                   &error);
+  g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED);
+  g_error_free (error);
+  g_assert (reply == NULL);
+
+  g_main_loop_quit (loop);
+}
+
+static void
+msg_on_connection_opened (GDBusConnection *connection,
+                          gpointer         user_data)
+{
+  g_main_loop_quit (loop);
+}
+
+static void
+msg_cb_expect_success (GDBusConnection *connection,
+                       GAsyncResult    *res,
+                       gpointer         user_data)
+{
+  GError *error;
+  DBusMessage *reply;
+
+  error = NULL;
+  reply = g_dbus_connection_send_dbus_1_message_with_reply_finish (connection,
+                                                                   res,
+                                                                   &error);
+  g_assert_no_error (error);
+  g_assert (reply != NULL);
+  dbus_message_unref (reply);
+
+  g_main_loop_quit (loop);
+}
+
+static void
+msg_cb_expect_error_cancelled (GDBusConnection *connection,
+                               GAsyncResult    *res,
+                               gpointer         user_data)
+{
+  GError *error;
+  DBusMessage *reply;
+
+  error = NULL;
+  reply = g_dbus_connection_send_dbus_1_message_with_reply_finish (connection,
+                                                                   res,
+                                                                   &error);
+  g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_CANCELLED);
+  g_error_free (error);
+  g_assert (reply == NULL);
+
+  g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+test_dbus_connection_send (void)
+{
+  GDBusConnection *c;
+  DBusMessage *m;
+  GCancellable *ca;
+
+  /* get an unopened connection */
+  c = g_dbus_connection_bus_get (G_BUS_TYPE_SESSION,
+                                 NULL,
+                                 (GAsyncReadyCallback) get_connection_callback_no_bus_exists,
+                                 NULL);
+  g_assert (!g_dbus_connection_get_is_open (c));
+  g_main_loop_run (loop);
+  g_assert (!g_dbus_connection_get_is_open (c));
+
+  m = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+                                    DBUS_PATH_DBUS,
+                                    DBUS_INTERFACE_DBUS,
+                                    "GetId");
+
+  /* check we get an error when sending on an unopened connection */
+  g_dbus_connection_send_dbus_1_message_with_reply (c,
+                                                    m,
+                                                    -1,
+                                                    NULL,
+                                                    (GAsyncReadyCallback) msg_cb_expect_error,
+                                                    NULL);
+  g_main_loop_run (loop);
+
+  /* check that we never try an op if the GCancellable is already cancelled - i.e. we should
+   * get G_DBUS_ERROR_CANCELLED instead of G_DBUS_ERROR_FAILED
+   */
+  ca = g_cancellable_new ();
+  g_cancellable_cancel (ca);
+  g_dbus_connection_send_dbus_1_message_with_reply (c,
+                                                    m,
+                                                    -1,
+                                                    ca,
+                                                    (GAsyncReadyCallback) msg_cb_expect_error_cancelled,
+                                                    NULL);
+  g_main_loop_run (loop);
+  g_object_unref (ca);
+
+  /* bring up the bus and try again */
+  g_signal_connect (c, "opened", G_CALLBACK (msg_on_connection_opened), NULL);
+  session_bus_up ();
+  g_main_loop_run (loop);
+  g_assert (g_dbus_connection_get_is_open (c));
+  g_signal_handlers_disconnect_by_func (c, G_CALLBACK (msg_on_connection_opened), NULL);
+  g_dbus_connection_send_dbus_1_message_with_reply (c,
+                                                    m,
+                                                    -1,
+                                                    NULL,
+                                                    (GAsyncReadyCallback) msg_cb_expect_success,
+                                                    NULL);
+  g_main_loop_run (loop);
+
+  /* check that cancellation works (the message is already in flight when we cancel) */
+  ca = g_cancellable_new ();
+  g_dbus_connection_send_dbus_1_message_with_reply (c,
+                                                    m,
+                                                    -1,
+                                                    ca,
+                                                    (GAsyncReadyCallback) msg_cb_expect_error_cancelled,
+                                                    NULL);
+  g_cancellable_cancel (ca);
+  g_main_loop_run (loop);
+  g_object_unref (ca);
+
+  /* clean up */
+  dbus_message_unref (m);
+  g_object_unref (c);
+  session_bus_down ();
+
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
 /* Test that GBusNameOwner works correctly */
 /* ---------------------------------------------------------------------------------------------------- */
 
@@ -1228,6 +1375,7 @@ main (int   argc,
   g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE);
 
   g_test_add_func ("/gdbus/message-bus-connections", test_message_bus_connections);
+  g_test_add_func ("/gdbus/dbus-connection-send", test_dbus_connection_send);
   g_test_add_func ("/gdbus/bus-name-owner", test_bus_name_owner);
   g_test_add_func ("/gdbus/bus-name-watcher", test_bus_name_watcher);
   return g_test_run();



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]