[glib/gdbus: 6/6] Add a method on GDBusConnection to send D-Bus messages
- From: David Zeuthen <davidz src gnome org>
- To: svn-commits-list gnome org
- Subject: [glib/gdbus: 6/6] Add a method on GDBusConnection to send D-Bus messages
- Date: Wed, 22 Apr 2009 11:45:29 -0400 (EDT)
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]