[glib] GDBusInterfaceSkeleton: make it possible to export on multiple connections



commit a00530ecb0e8576e7a023f37a97528da4d0dfce5
Author: Tristan Van Berkom <tristanvb openismus com>
Date:   Fri Dec 2 11:20:21 2011 -0500

    GDBusInterfaceSkeleton: make it possible to export on multiple connections
    
    This is useful in peer-to-peer connections.
    
    With minor changes by David Zeuthen <davidz redhat com>.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=662718
    
    Signed-off-by: David Zeuthen <davidz redhat com>

 docs/reference/gio/gio-sections.txt |    3 +
 gio/gdbus-2.0/codegen/codegen.py    |   56 ++++--
 gio/gdbusinterfaceskeleton.c        |  364 ++++++++++++++++++++++++++++-------
 gio/gdbusinterfaceskeleton.h        |    6 +
 gio/gio.symbols                     |    3 +
 gio/tests/Makefile.am               |    8 +-
 gio/tests/gdbus-peer.c              |  239 +++++++++++++++++++++++
 7 files changed, 592 insertions(+), 87 deletions(-)
---
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index 895398d..6dfaf9a 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -3344,7 +3344,10 @@ g_dbus_interface_skeleton_get_vtable
 g_dbus_interface_skeleton_get_properties
 g_dbus_interface_skeleton_export
 g_dbus_interface_skeleton_unexport
+g_dbus_interface_skeleton_unexport_from_connection
 g_dbus_interface_skeleton_get_connection
+g_dbus_interface_skeleton_get_connections
+g_dbus_interface_skeleton_has_connection
 g_dbus_interface_skeleton_get_object_path
 GDBusInterfaceSkeletonFlags
 g_dbus_interface_skeleton_get_flags
diff --git a/gio/gdbus-2.0/codegen/codegen.py b/gio/gdbus-2.0/codegen/codegen.py
index 8d34bdd..41161b0 100644
--- a/gio/gdbus-2.0/codegen/codegen.py
+++ b/gio/gdbus-2.0/codegen/codegen.py
@@ -2307,21 +2307,31 @@ class CodeGenerator:
                 self.c.write(',\n    %sarg_%s'%(a.ctype_in, a.name))
             self.c.write(')\n'
                          '{\n'
-                         '  %sSkeleton *skeleton = %s%s_SKELETON (object);\n'
-                         '  GDBusConnection *connection = g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (skeleton));\n'
+                         '  %sSkeleton *skeleton = %s%s_SKELETON (object);\n\n'
+                         '  GList      *connections, *l;\n'
+                         '  GVariant   *signal_variant;\n'
+                         '  connections = g_dbus_interface_skeleton_get_connections (G_DBUS_INTERFACE_SKELETON (skeleton));\n'
                          %(i.camel_name, i.ns_upper, i.name_upper))
-            self.c.write('  if (connection == NULL)\n'
-                         '    return;\n'
-                         '  g_dbus_connection_emit_signal (connection,\n'
-                         '    NULL, g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (skeleton)), "%s", "%s",\n'
-                         '    g_variant_new ("('
-                         %(i.name, s.name))
+            self.c.write('\n'
+                         '  signal_variant = g_variant_ref_sink (g_variant_new ("(')
             for a in s.args:
                 self.c.write('%s'%(a.format_in))
             self.c.write(')"')
             for a in s.args:
                 self.c.write(',\n                   arg_%s'%(a.name))
-            self.c.write('), NULL);\n')
+            self.c.write('));\n')
+
+            self.c.write('  for (l = connections; l != NULL; l = l->next)\n'
+                         '    {\n'
+                         '      GDBusConnection *connection = l->data;\n'
+                         '      g_dbus_connection_emit_signal (connection,\n'
+                         '        NULL, g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (skeleton)), "%s", "%s",\n'
+                         '        signal_variant, NULL);\n'
+                         '    }\n'
+                         %(i.name, s.name))
+            self.c.write('  g_variant_unref (signal_variant);\n')
+            self.c.write('  g_list_foreach (connections, (GFunc)g_object_unref, NULL);\n')
+            self.c.write('  g_list_free (connections);\n')
             self.c.write('}\n'
                          '\n')
 
@@ -2407,14 +2417,26 @@ class CodeGenerator:
                          '    }\n'
                          '  if (num_changes > 0)\n'
                          '    {\n'
-                         '      g_dbus_connection_emit_signal (g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (skeleton)),\n'
-                         '                                     NULL, g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (skeleton)),\n'
-                         '                                     "org.freedesktop.DBus.Properties",\n'
-                         '                                     "PropertiesChanged",\n'
-                         '                                     g_variant_new ("(sa{sv}as)",\n'
-                         '                                                    "%s",\n'
-                         '                                                    &builder, &invalidated_builder),\n'
-                         '                                     NULL);\n'
+                         '      GList *connections, *l;\n'
+                         '      GVariant *signal_variant;'
+                         '\n'
+                         '      signal_variant = g_variant_ref_sink (g_variant_new ("(sa{sv}as)", "%s",\n'
+                         '                                           &builder, &invalidated_builder));\n'
+                         '      connections = g_dbus_interface_skeleton_get_connections (G_DBUS_INTERFACE_SKELETON (skeleton));\n'
+                         '      for (l = connections; l != NULL; l = l->next)\n'
+                         '        {\n'
+                         '          GDBusConnection *connection = l->data;\n'
+                         '\n'
+                         '          g_dbus_connection_emit_signal (connection,\n'
+                         '                                         NULL, g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (skeleton)),\n'
+                         '                                         "org.freedesktop.DBus.Properties",\n'
+                         '                                         "PropertiesChanged",\n'
+                         '                                         signal_variant,\n'
+                         '                                         NULL);\n'
+                         '        }\n'
+                         '      g_variant_unref (signal_variant);\n'
+                         '      g_list_foreach (connections, (GFunc)g_object_unref, NULL);\n'
+                         '      g_list_free (connections);\n'
                          '    }\n'
                          '  else\n'
                          '    {\n'
diff --git a/gio/gdbusinterfaceskeleton.c b/gio/gdbusinterfaceskeleton.c
index 0cd5d6f..e6c0a49 100644
--- a/gio/gdbusinterfaceskeleton.c
+++ b/gio/gdbusinterfaceskeleton.c
@@ -44,17 +44,22 @@
 
 struct _GDBusInterfaceSkeletonPrivate
 {
-  GMutex lock;
+  GMutex                      lock;
 
-  GDBusObject *object;
+  GDBusObject                *object;
   GDBusInterfaceSkeletonFlags flags;
-  guint registration_id;
 
-  GDBusConnection *connection;
-  gchar *object_path;
-  GDBusInterfaceVTable *hooked_vtable;
+  GSList                     *connections;   /* List of ConnectionData */
+  gchar                      *object_path;   /* The object path for this skeleton */
+  GDBusInterfaceVTable       *hooked_vtable;
 };
 
+typedef struct
+{
+  GDBusConnection *connection;
+  guint            registration_id;
+} ConnectionData;
+
 enum
 {
   G_AUTHORIZE_METHOD_SIGNAL,
@@ -69,7 +74,21 @@ enum
 
 static guint signals[LAST_SIGNAL] = {0};
 
-static void dbus_interface_interface_init (GDBusInterfaceIface *iface);
+static void     dbus_interface_interface_init                      (GDBusInterfaceIface    *iface);
+
+static void     set_object_path_locked                             (GDBusInterfaceSkeleton *interface_,
+                                                                    const gchar            *object_path);
+static void     remove_connection_locked                           (GDBusInterfaceSkeleton *interface_,
+                                                                    GDBusConnection        *connection);
+static void     skeleton_intercept_handle_method_call              (GDBusConnection        *connection,
+                                                                    const gchar            *sender,
+                                                                    const gchar            *object_path,
+                                                                    const gchar            *interface_name,
+                                                                    const gchar            *method_name,
+                                                                    GVariant               *parameters,
+                                                                    GDBusMethodInvocation  *invocation,
+                                                                    gpointer                user_data);
+
 
 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GDBusInterfaceSkeleton, g_dbus_interface_skeleton, G_TYPE_OBJECT,
                                   G_IMPLEMENT_INTERFACE (G_TYPE_DBUS_INTERFACE, dbus_interface_interface_init));
@@ -78,13 +97,22 @@ static void
 g_dbus_interface_skeleton_finalize (GObject *object)
 {
   GDBusInterfaceSkeleton *interface = G_DBUS_INTERFACE_SKELETON (object);
-  /* unexport if already exported */
-  if (interface->priv->registration_id > 0)
-    g_dbus_interface_skeleton_unexport (interface);
 
-  g_assert (interface->priv->connection == NULL);
-  g_assert (interface->priv->object_path == NULL);
-  g_assert (interface->priv->hooked_vtable == NULL);
+  /* Hold the lock just incase any code we call verifies that the lock is held */
+  g_mutex_lock (&interface->priv->lock);
+
+  /* unexport from all connections if we're exported anywhere */
+  while (interface->priv->connections != NULL)
+    {
+      ConnectionData *data = interface->priv->connections->data;
+      remove_connection_locked (interface, data->connection);
+    }
+
+  set_object_path_locked (interface, NULL);
+
+  g_mutex_unlock (&interface->priv->lock);
+
+  g_free (interface->priv->hooked_vtable);
 
   if (interface->priv->object != NULL)
     g_object_remove_weak_pointer (G_OBJECT (interface->priv->object), (gpointer *) &interface->priv->object);
@@ -617,11 +645,112 @@ skeleton_intercept_handle_method_call (GDBusConnection       *connection,
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+static ConnectionData *
+new_connection (GDBusConnection *connection,
+                guint            registration_id)
+{
+  ConnectionData *data;
+
+  data = g_slice_new0 (ConnectionData);
+  data->connection      = g_object_ref (connection);
+  data->registration_id = registration_id;
+
+  return data;
+}
+
+static void
+free_connection (ConnectionData *data)
+{
+  if (data != NULL)
+    {
+      g_object_unref (data->connection);
+      g_slice_free (ConnectionData, data);
+    }
+}
+
+static gboolean
+add_connection_locked (GDBusInterfaceSkeleton *interface_,
+                       GDBusConnection        *connection,
+                       GError                **error)
+{
+  ConnectionData *data;
+  guint registration_id;
+  gboolean ret = FALSE;
+
+  if (interface_->priv->hooked_vtable == NULL)
+    {
+      /* Hook the vtable since we need to intercept method calls for
+       * ::g-authorize-method and for dispatching in thread vs
+       * context
+       *
+       * We need to wait until subclasses have had time to initialize
+       * properly before building the hooked_vtable, so we create it
+       * once at the last minute.
+       */
+      interface_->priv->hooked_vtable = g_memdup (g_dbus_interface_skeleton_get_vtable (interface_), sizeof (GDBusInterfaceVTable));
+      interface_->priv->hooked_vtable->method_call = skeleton_intercept_handle_method_call;
+    }
+
+  registration_id = g_dbus_connection_register_object (connection,
+                                                       interface_->priv->object_path,
+                                                       g_dbus_interface_skeleton_get_info (interface_),
+                                                       interface_->priv->hooked_vtable,
+                                                       interface_,
+                                                       NULL, /* user_data_free_func */
+                                                       error);
+
+  if (registration_id > 0)
+    {
+      data = new_connection (connection, registration_id);
+      interface_->priv->connections = g_slist_append (interface_->priv->connections, data);
+      ret = TRUE;
+    }
+
+  return ret;
+}
+
+static void
+remove_connection_locked (GDBusInterfaceSkeleton *interface_,
+                          GDBusConnection        *connection)
+{
+  ConnectionData *data;
+  GSList *l;
+
+  /* Get the connection in the list and unregister ... */
+  for (l = interface_->priv->connections; l != NULL; l = l->next)
+    {
+      data = l->data;
+      if (data->connection == connection)
+        {
+          g_warn_if_fail (g_dbus_connection_unregister_object (data->connection, data->registration_id));
+          free_connection (data);
+          interface_->priv->connections = g_slist_delete_link (interface_->priv->connections, l);
+          /* we are guaranteed that the connection is only added once, so bail out early */
+          goto out;
+        }
+    }
+ out:
+  ;
+}
+
+static void
+set_object_path_locked (GDBusInterfaceSkeleton *interface_,
+                        const gchar            *object_path)
+{
+  if (g_strcmp0 (interface_->priv->object_path, object_path) != 0)
+    {
+      g_free (interface_->priv->object_path);
+      interface_->priv->object_path = g_strdup (object_path);
+    }
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
 /**
  * g_dbus_interface_skeleton_get_connection:
  * @interface_: A #GDBusInterfaceSkeleton.
  *
- * Gets the connection that @interface_ is exported on, if any.
+ * Gets the first connection that @interface_ is exported on, if any.
  *
  * Returns: (transfer none): A #GDBusConnection or %NULL if @interface_ is
  * not exported anywhere. Do not free, the object belongs to @interface_.
@@ -631,10 +760,97 @@ skeleton_intercept_handle_method_call (GDBusConnection       *connection,
 GDBusConnection *
 g_dbus_interface_skeleton_get_connection (GDBusInterfaceSkeleton *interface_)
 {
+  ConnectionData  *data;
   GDBusConnection *ret;
+
   g_return_val_if_fail (G_IS_DBUS_INTERFACE_SKELETON (interface_), NULL);
   g_mutex_lock (&interface_->priv->lock);
-  ret = interface_->priv->connection;
+
+  ret = NULL;
+  if (interface_->priv->connections != NULL)
+    {
+      data = interface_->priv->connections->data;
+      if (data != NULL)
+        ret = data->connection;
+    }
+
+  g_mutex_unlock (&interface_->priv->lock);
+
+  return ret;
+}
+
+/**
+ * g_dbus_interface_skeleton_get_connections:
+ * @interface_: A #GDBusInterfaceSkeleton.
+ *
+ * Gets a list of the connections that @interface_ is exported on.
+ *
+ * Returns: (element-type GDBusConnection) (transfer full): A list of
+ *   all the connections that @interface_ is exported on. The returned
+ *   list should be freed with g_list_free() after each element has
+ *   been freed with g_object_unref().
+ *
+ * Since: 2.32
+ */
+GList *
+g_dbus_interface_skeleton_get_connections (GDBusInterfaceSkeleton *interface_)
+{
+  GList           *connections;
+  GSList          *l;
+  ConnectionData  *data;
+
+  g_return_val_if_fail (G_IS_DBUS_INTERFACE_SKELETON (interface_), NULL);
+
+  g_mutex_lock (&interface_->priv->lock);
+  connections = NULL;
+
+  for (l = interface_->priv->connections; l != NULL; l = l->next)
+    {
+      data        = l->data;
+      connections = g_list_prepend (connections,
+                                    /* Return a reference to each connection */
+                                    g_object_ref (data->connection));
+    }
+
+  g_mutex_unlock (&interface_->priv->lock);
+
+  return g_list_reverse (connections);
+}
+
+/**
+ * g_dbus_interface_skeleton_has_connection:
+ * @interface_: A #GDBusInterfaceSkeleton.
+ * @connection: A #GDBusConnection.
+ *
+ * Checks if @interface_ is export on @connection.
+ *
+ * Returns: %TRUE if @interface_ is exported on @connection, %FALSE otherwise.
+ *
+ * Since: 2.32
+ */
+gboolean
+g_dbus_interface_skeleton_has_connection (GDBusInterfaceSkeleton     *interface_,
+                                          GDBusConnection            *connection)
+{
+  GSList *l;
+  gboolean ret = FALSE;
+
+  g_return_val_if_fail (G_IS_DBUS_INTERFACE_SKELETON (interface_), FALSE);
+  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
+
+  g_mutex_lock (&interface_->priv->lock);
+
+  for (l = interface_->priv->connections; l != NULL; l = l->next)
+    {
+      ConnectionData *data = l->data;
+      if (data->connection == connection)
+        {
+          ret = TRUE;
+          goto out;
+        }
+    }
+
+ out:
   g_mutex_unlock (&interface_->priv->lock);
   return ret;
 }
@@ -670,9 +886,13 @@ g_dbus_interface_skeleton_get_object_path (GDBusInterfaceSkeleton *interface_)
  *
  * Exports @interface_ at @object_path on @connection.
  *
+ * This can be called multiple times to export the same @interface_
+ * onto multiple connections however the @object_path provided must be
+ * the same for all connections.
+ *
  * Use g_dbus_interface_skeleton_unexport() to unexport the object.
  *
- * Returns: %TRUE if the interface was exported, other %FALSE with
+ * Returns: %TRUE if the interface was exported on @connection, otherwise %FALSE with
  * @error set.
  *
  * Since: 2.30
@@ -683,51 +903,25 @@ g_dbus_interface_skeleton_export (GDBusInterfaceSkeleton  *interface_,
                                   const gchar             *object_path,
                                   GError                 **error)
 {
-  gboolean ret;
+  gboolean ret = FALSE;
 
-  g_return_val_if_fail (G_IS_DBUS_INTERFACE_SKELETON (interface_), 0);
-  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0);
-  g_return_val_if_fail (g_variant_is_object_path (object_path), 0);
-  g_return_val_if_fail (error == NULL || *error == NULL, 0);
+  g_return_val_if_fail (G_IS_DBUS_INTERFACE_SKELETON (interface_), FALSE);
+  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
+  g_return_val_if_fail (g_variant_is_object_path (object_path), FALSE);
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
-  g_mutex_lock (&interface_->priv->lock);
+  /* Assert that the object path is the same for multiple connections here */
+  g_return_val_if_fail (interface_->priv->object_path == NULL ||
+                        g_strcmp0 (interface_->priv->object_path, object_path) == 0, FALSE);
 
-  ret = FALSE;
-  if (interface_->priv->registration_id > 0)
-    {
-      g_set_error_literal (error,
-                           G_IO_ERROR,
-                           G_IO_ERROR_FAILED, /* TODO: new error code */
-                           "The object is already exported");
-      goto out;
-    }
+  g_mutex_lock (&interface_->priv->lock);
 
-  g_assert (interface_->priv->connection == NULL);
-  g_assert (interface_->priv->object_path == NULL);
-  g_assert (interface_->priv->hooked_vtable == NULL);
+  /* Set the object path */
+  set_object_path_locked (interface_, object_path);
 
-  /* Hook the vtable since we need to intercept method calls for
-   * ::g-authorize-method and for dispatching in thread vs
-   * context
-   */
-  interface_->priv->hooked_vtable = g_memdup (g_dbus_interface_skeleton_get_vtable (interface_), sizeof (GDBusInterfaceVTable));
-  interface_->priv->hooked_vtable->method_call = skeleton_intercept_handle_method_call;
-
-  interface_->priv->connection = g_object_ref (connection);
-  interface_->priv->object_path = g_strdup (object_path);
-  interface_->priv->registration_id = g_dbus_connection_register_object (connection,
-                                                                         object_path,
-                                                                         g_dbus_interface_skeleton_get_info (interface_),
-                                                                         interface_->priv->hooked_vtable,
-                                                                         interface_,
-                                                                         NULL, /* user_data_free_func */
-                                                                         error);
-  if (interface_->priv->registration_id == 0)
-    goto out;
-
-  ret = TRUE;
+  /* Add the connection */
+  ret = add_connection_locked (interface_, connection, error);
 
- out:
   g_mutex_unlock (&interface_->priv->lock);
   return ret;
 }
@@ -736,8 +930,10 @@ g_dbus_interface_skeleton_export (GDBusInterfaceSkeleton  *interface_,
  * g_dbus_interface_skeleton_unexport:
  * @interface_: A #GDBusInterfaceSkeleton.
  *
- * Stops exporting an interface previously exported with
- * g_dbus_interface_skeleton_export().
+ * Stops exporting @interface_ on all connections it is exported on.
+ *
+ * To unexport @interface_ from only a single connection, use
+ * g_dbus_interface_skeleton_export_from_connection()
  *
  * Since: 2.30
  */
@@ -745,23 +941,57 @@ void
 g_dbus_interface_skeleton_unexport (GDBusInterfaceSkeleton *interface_)
 {
   g_return_if_fail (G_IS_DBUS_INTERFACE_SKELETON (interface_));
-  g_return_if_fail (interface_->priv->registration_id > 0);
+  g_return_if_fail (interface_->priv->connections != NULL);
+
+  g_mutex_lock (&interface_->priv->lock);
+
+  g_assert (interface_->priv->object_path != NULL);
+  g_assert (interface_->priv->hooked_vtable != NULL);
+
+  /* Remove all connections */
+  while (interface_->priv->connections != NULL)
+    {
+      ConnectionData *data = interface_->priv->connections->data;
+      remove_connection_locked (interface_, data->connection);
+    }
+
+  /* Unset the object path since there are no connections left */
+  set_object_path_locked (interface_, NULL);
+
+  g_mutex_unlock (&interface_->priv->lock);
+}
+
+
+/**
+ * g_dbus_interface_skeleton_unexport_from_connection:
+ * @interface_: A #GDBusInterfaceSkeleton.
+ * @connection: A #GDBusConnection.
+ *
+ * Stops exporting @interface_ on @connection.
+ *
+ * To stop exporting on all connections the interface is exported on,
+ * use g_dbus_interface_skeleton_unexport().
+ *
+ * Since: 2.32
+ */
+void
+g_dbus_interface_skeleton_unexport_from_connection (GDBusInterfaceSkeleton *interface_,
+                                                    GDBusConnection        *connection)
+{
+  g_return_if_fail (G_IS_DBUS_INTERFACE_SKELETON (interface_));
+  g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
+  g_return_if_fail (interface_->priv->connections != NULL);
 
   g_mutex_lock (&interface_->priv->lock);
 
-  g_assert (interface_->priv->connection != NULL);
   g_assert (interface_->priv->object_path != NULL);
   g_assert (interface_->priv->hooked_vtable != NULL);
 
-  g_warn_if_fail (g_dbus_connection_unregister_object (interface_->priv->connection,
-                                                       interface_->priv->registration_id));
+  remove_connection_locked (interface_, connection);
 
-  g_object_unref (interface_->priv->connection);
-  g_free (interface_->priv->object_path);
-  interface_->priv->connection = NULL;
-  interface_->priv->object_path = NULL;
-  interface_->priv->hooked_vtable = NULL;
-  interface_->priv->registration_id = 0;
+  /* Reset the object path if we removed the last connection */
+  if (interface_->priv->connections == NULL)
+    set_object_path_locked (interface_, NULL);
 
   g_mutex_unlock (&interface_->priv->lock);
 }
diff --git a/gio/gdbusinterfaceskeleton.h b/gio/gdbusinterfaceskeleton.h
index 69dcc2f..5f1c166 100644
--- a/gio/gdbusinterfaceskeleton.h
+++ b/gio/gdbusinterfaceskeleton.h
@@ -101,7 +101,13 @@ gboolean                     g_dbus_interface_skeleton_export          (GDBusInt
                                                                         const gchar                 *object_path,
                                                                         GError                     **error);
 void                         g_dbus_interface_skeleton_unexport        (GDBusInterfaceSkeleton      *interface_);
+void                g_dbus_interface_skeleton_unexport_from_connection (GDBusInterfaceSkeleton      *interface_,
+                                                                        GDBusConnection             *connection);
+
 GDBusConnection             *g_dbus_interface_skeleton_get_connection  (GDBusInterfaceSkeleton      *interface_);
+GList                       *g_dbus_interface_skeleton_get_connections (GDBusInterfaceSkeleton      *interface_);
+gboolean                     g_dbus_interface_skeleton_has_connection  (GDBusInterfaceSkeleton      *interface_,
+                                                                        GDBusConnection             *connection);
 const gchar                 *g_dbus_interface_skeleton_get_object_path (GDBusInterfaceSkeleton      *interface_);
 
 G_END_DECLS
diff --git a/gio/gio.symbols b/gio/gio.symbols
index 791fbeb..cd8d52a 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -1535,6 +1535,8 @@ g_dbus_interface_skeleton_export
 g_dbus_interface_skeleton_flags_get_type
 g_dbus_interface_skeleton_flush
 g_dbus_interface_skeleton_get_connection
+g_dbus_interface_skeleton_get_connections
+g_dbus_interface_skeleton_has_connection
 g_dbus_interface_skeleton_get_flags
 g_dbus_interface_skeleton_get_info
 g_dbus_interface_skeleton_get_object_path
@@ -1543,6 +1545,7 @@ g_dbus_interface_skeleton_get_type
 g_dbus_interface_skeleton_get_vtable
 g_dbus_interface_skeleton_set_flags
 g_dbus_interface_skeleton_unexport
+g_dbus_interface_skeleton_unexport_from_connection
 g_dbus_object_get_interface
 g_dbus_object_get_interfaces
 g_dbus_object_get_object_path
diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am
index 9724892..b449a38 100644
--- a/gio/tests/Makefile.am
+++ b/gio/tests/Makefile.am
@@ -344,9 +344,6 @@ gdbus_export_LDADD = $(progs_ldadd)
 gdbus_error_SOURCES = gdbus-error.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c
 gdbus_error_LDADD = $(progs_ldadd)
 
-gdbus_peer_SOURCES = gdbus-peer.c gdbus-tests.h gdbus-tests.c
-gdbus_peer_LDADD = $(progs_ldadd)
-
 gdbus_non_socket_SOURCES = \
 	gdbus-non-socket.c \
 	gdbus-tests.c \
@@ -527,6 +524,11 @@ gdbus_example_objectmanager_server_LDADD    = $(top_builddir)/gio/tests/gdbus-ob
 gdbus_example_objectmanager_client_SOURCES  = gdbus-example-objectmanager-client.c
 gdbus_example_objectmanager_client_CFLAGS   = -I$(top_builddir)/gio/tests/gdbus-object-manager-example
 gdbus_example_objectmanager_client_LDADD    = $(top_builddir)/gio/tests/gdbus-object-manager-example/libgdbus-example-objectmanager.la $(progs_ldadd)
+
+gdbus_peer_SOURCES = gdbus-peer.c gdbus-tests.h gdbus-tests.c
+gdbus_peer_CFLAGS  = -I$(top_builddir)/gio/tests/gdbus-object-manager-example
+gdbus_peer_LDADD   = $(top_builddir)/gio/tests/gdbus-object-manager-example/libgdbus-example-objectmanager.la $(progs_ldadd)
+
 endif OS_UNIX
 
 # -----------------------------------------------------------------------------
diff --git a/gio/tests/gdbus-peer.c b/gio/tests/gdbus-peer.c
index ee2ceb6..7eeb59d 100644
--- a/gio/tests/gdbus-peer.c
+++ b/gio/tests/gdbus-peer.c
@@ -47,6 +47,8 @@
 
 #include "gdbus-tests.h"
 
+#include "gdbus-example-objectmanager-generated.h"
+
 #ifdef G_OS_UNIX
 static gboolean is_unix = TRUE;
 #else
@@ -601,6 +603,7 @@ test_peer (void)
   g_assert (c == NULL);
 
   /* bring up a server - we run the server in a different thread to avoid deadlocks */
+  service_loop = NULL;
   service_thread = g_thread_new ("test_peer",
                                  service_thread_func,
                                  &data);
@@ -1534,6 +1537,241 @@ test_tcp_anonymous (void)
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+static GDBusServer *codegen_server = NULL;
+
+static gboolean
+codegen_on_animal_poke (ExampleAnimal          *animal,
+                        GDBusMethodInvocation  *invocation,
+                        gboolean                make_sad,
+                        gboolean                make_happy,
+                        gpointer                user_data)
+{
+  if ((make_sad && make_happy) || (!make_sad && !make_happy))
+    {
+      g_main_loop_quit (service_loop);
+
+      g_dbus_method_invocation_return_dbus_error (invocation,
+                                                  "org.gtk.GDBus.Examples.ObjectManager.Error.Failed",
+                                                  "Exactly one of make_sad or make_happy must be TRUE");
+      goto out;
+    }
+
+  if (make_sad)
+    {
+      if (g_strcmp0 (example_animal_get_mood (animal), "Sad") == 0)
+        {
+          g_dbus_method_invocation_return_dbus_error (invocation,
+                                                      "org.gtk.GDBus.Examples.ObjectManager.Error.SadAnimalIsSad",
+                                                      "Sad animal is already sad");
+          goto out;
+        }
+
+      example_animal_set_mood (animal, "Sad");
+      example_animal_complete_poke (animal, invocation);
+      goto out;
+    }
+
+  if (make_happy)
+    {
+      if (g_strcmp0 (example_animal_get_mood (animal), "Happy") == 0)
+        {
+          g_dbus_method_invocation_return_dbus_error (invocation,
+                                                      "org.gtk.GDBus.Examples.ObjectManager.Error.HappyAnimalIsHappy",
+                                                      "Happy animal is already happy");
+          goto out;
+        }
+
+      example_animal_set_mood (animal, "Happy");
+      example_animal_complete_poke (animal, invocation);
+      goto out;
+    }
+
+  g_assert_not_reached ();
+
+ out:
+  return TRUE; /* to indicate that the method was handled */
+}
+
+/* Runs in thread we created GDBusServer in (since we didn't pass G_DBUS_SERVER_FLAGS_RUN_IN_THREAD) */
+static gboolean
+codegen_on_new_connection (GDBusServer *server,
+                           GDBusConnection *connection,
+                           gpointer user_data)
+{
+  ExampleAnimal *animal = user_data;
+  GError        *error = NULL;
+
+  /* g_print ("Client connected.\n" */
+  /*          "Negotiated capabilities: unix-fd-passing=%d\n", */
+  /*          g_dbus_connection_get_capabilities (connection) & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING); */
+
+  g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (animal), connection,
+                                    "/Example/Animals/000", &error);
+  g_assert_no_error (error);
+
+  return TRUE;
+}
+
+static gpointer
+codegen_service_thread_func (gpointer user_data)
+{
+  GMainContext   *service_context;
+  ExampleAnimal  *animal;
+  GError         *error = NULL;
+
+  service_context = g_main_context_new ();
+  g_main_context_push_thread_default (service_context);
+
+  /* Create the animal in the right thread context */
+  animal = example_animal_skeleton_new ();
+
+  /* Handle Poke() D-Bus method invocations on the .Animal interface */
+  g_signal_connect (animal, "handle-poke",
+                    G_CALLBACK (codegen_on_animal_poke),
+                    NULL); /* user_data */
+
+  codegen_server = g_dbus_server_new_sync (tmp_address,
+                                           G_DBUS_SERVER_FLAGS_NONE,
+                                           test_guid,
+                                           NULL, /* observer */
+                                           NULL, /* cancellable */
+                                           &error);
+  g_assert_no_error (error);
+  g_dbus_server_start (codegen_server);
+
+  g_signal_connect (codegen_server, "new-connection",
+                    G_CALLBACK (codegen_on_new_connection),
+                    animal);
+
+  service_loop = g_main_loop_new (service_context, FALSE);
+  g_main_loop_run (service_loop);
+
+  g_object_unref (animal);
+
+  g_main_context_pop_thread_default (service_context);
+
+  g_main_loop_unref (service_loop);
+  g_main_context_unref (service_context);
+
+  g_dbus_server_stop (codegen_server);
+  g_object_unref (codegen_server);
+  codegen_server = NULL;
+
+  return NULL;
+}
+
+
+gboolean
+codegen_quit_mainloop_timeout (gpointer data)
+{
+  g_main_loop_quit (loop);
+  return FALSE;
+}
+
+static void
+codegen_test_peer (void)
+{
+  GDBusConnection     *connection;
+  ExampleAnimal       *animal1, *animal2;
+  GThread             *service_thread;
+  GError              *error = NULL;
+  GVariant            *value;
+
+  /* bring up a server - we run the server in a different thread to avoid deadlocks */
+  service_thread = g_thread_new ("codegen_test_peer",
+                                 codegen_service_thread_func,
+                                 NULL);
+  service_loop = NULL;
+  while (service_loop == NULL)
+    g_thread_yield ();
+  g_assert (codegen_server != NULL);
+
+  /* Get an animal 1 ...  */
+  connection = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (codegen_server),
+                                                       G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
+                                                       NULL, /* GDBusAuthObserver */
+                                                       NULL, /* cancellable */
+                                                       &error);
+  g_assert_no_error (error);
+  g_assert (connection != NULL);
+
+  animal1 = example_animal_proxy_new_sync (connection, 0, NULL,
+                                           "/Example/Animals/000", NULL, &error);
+  g_assert_no_error (error);
+  g_assert (animal1 != NULL);
+  g_object_unref (connection);
+
+  /* Get animal 2 ...  */
+  connection = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (codegen_server),
+                                                       G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
+                                                       NULL, /* GDBusAuthObserver */
+                                                       NULL, /* cancellable */
+                                                       &error);
+  g_assert_no_error (error);
+  g_assert (connection != NULL);
+
+  animal2 = example_animal_proxy_new_sync (connection, 0, NULL,
+                                           "/Example/Animals/000", NULL, &error);
+  g_assert_no_error (error);
+  g_assert (animal2 != NULL);
+  g_object_unref (connection);
+
+  /* Make animal sad via animal1  */
+  example_animal_call_poke_sync (animal1, TRUE, FALSE, NULL, &error);
+  g_assert_no_error (error);
+
+  /* Poke server and make sure animal is updated */
+  value = g_dbus_proxy_call_sync (G_DBUS_PROXY (animal1),
+                                  "org.freedesktop.DBus.Peer.Ping",
+                                  NULL, G_DBUS_CALL_FLAGS_NONE, -1,
+                                  NULL, &error);
+  g_assert_no_error (error);
+  g_assert (value != NULL);
+  g_variant_unref (value);
+
+  /* Give the proxies a chance to refresh in the defaul main loop */
+  g_timeout_add (100, codegen_quit_mainloop_timeout, NULL);
+  g_main_loop_run (loop);
+
+  /* Assert animals are sad */
+  g_assert_cmpstr (example_animal_get_mood (animal1), ==, "Sad");
+  g_assert_cmpstr (example_animal_get_mood (animal2), ==, "Sad");
+
+  /* Make animal happy via animal2  */
+  example_animal_call_poke_sync (animal2, FALSE, TRUE, NULL, &error);
+  g_assert_no_error (error);
+
+  /* Poke server and make sure animal is updated */
+  value = g_dbus_proxy_call_sync (G_DBUS_PROXY (animal2),
+                                  "org.freedesktop.DBus.Peer.Ping",
+                                  NULL, G_DBUS_CALL_FLAGS_NONE, -1,
+                                  NULL, &error);
+  g_assert_no_error (error);
+  g_assert (value != NULL);
+  g_variant_unref (value);
+
+  /* Give the proxies a chance to refresh in the defaul main loop */
+  g_timeout_add (1000, codegen_quit_mainloop_timeout, NULL);
+  g_main_loop_run (loop);
+
+  /* Assert animals are happy */
+  g_assert_cmpstr (example_animal_get_mood (animal1), ==, "Happy");
+  g_assert_cmpstr (example_animal_get_mood (animal2), ==, "Happy");
+
+  /* This final call making the animal happy and sad will cause
+   * the server to quit, when the server quits we dont get property
+   * change notifications anyway because those are done from an idle handler
+   */
+  example_animal_call_poke_sync (animal2, TRUE, TRUE, NULL, &error);
+
+  g_object_unref (animal1);
+  g_object_unref (animal2);
+  g_thread_join (service_thread);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+
 int
 main (int   argc,
       char *argv[])
@@ -1575,6 +1813,7 @@ main (int   argc,
   g_test_add_func ("/gdbus/tcp-anonymous", test_tcp_anonymous);
   g_test_add_func ("/gdbus/credentials", test_credentials);
   g_test_add_func ("/gdbus/overflow", test_overflow);
+  g_test_add_func ("/gdbus/codegen-peer-to-peer", codegen_test_peer);
 
   ret = g_test_run();
 



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