hook gvariant vectors up to kdbus
[platform/upstream/glib.git] / gio / gdbusobjectmanagerclient.c
index 74062e6..5755e74 100644 (file)
@@ -13,9 +13,7 @@
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General
- * Public License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
  *
  * Author: David Zeuthen <davidz@redhat.com>
  */
@@ -26,7 +24,6 @@
 #include "gdbusobjectmanagerclient.h"
 #include "gdbusobject.h"
 #include "gdbusprivate.h"
-#include "gio-marshal.h"
 #include "gioenumtypes.h"
 #include "ginitable.h"
 #include "gasyncresult.h"
@@ -48,8 +45,8 @@
  *
  * #GDBusObjectManagerClient is used to create, monitor and delete object
  * proxies for remote objects exported by a #GDBusObjectManagerServer (or any
- * code implementing the <ulink
- * url="http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager">org.freedesktop.DBus.ObjectManager</ulink>
+ * code implementing the
+ * [org.freedesktop.DBus.ObjectManager](http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager)
  * interface).
  *
  * Once an instance of this type has been created, you can connect to
  * is set to the new name owner (this includes emission of the
  * #GObject::notify signal).  Furthermore, you are guaranteed that
  * #GDBusObjectManagerClient:name-owner will alternate between a name owner
- * (e.g. <literal>:1.42</literal>) and %NULL even in the case where
+ * (e.g. `:1.42`) and %NULL even in the case where
  * the name of interest is atomically replaced
  *
  * Ultimately, #GDBusObjectManagerClient is used to obtain #GDBusProxy
  * instances. All signals (including the
- * <literal>org.freedesktop.DBus.Properties::PropertiesChanged</literal>
- * signal) delivered to #GDBusProxy instances are guaranteed to
- * originate from the name owner. This guarantee along with the
- * behavior described above, means that certain race conditions
- * including the <emphasis><quote>half the proxy is from the old owner
- * and the other half is from the new owner</quote></emphasis> problem
- * cannot happen.
+ * org.freedesktop.DBus.Properties::PropertiesChanged signal)
+ * delivered to #GDBusProxy instances are guaranteed to originate
+ * from the name owner. This guarantee along with the behavior
+ * described above, means that certain race conditions including the
+ * "half the proxy is from the old owner and the other half is from
+ * the new owner" problem cannot happen.
  *
  * To avoid having the application connect to signals on the returned
  * #GDBusObjectProxy and #GDBusProxy objects, the
  * #GDBusObjectManagerClient::interface-proxy-signal.
  *
  * Note that all callbacks and signals are emitted in the
- * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * [thread-default main context][g-main-context-push-thread-default]
  * that the #GDBusObjectManagerClient object was constructed
  * in. Additionally, the #GDBusObjectProxy and #GDBusProxy objects
  * originating from the #GDBusObjectManagerClient object will be created in
 
 struct _GDBusObjectManagerClientPrivate
 {
+  GMutex lock;
+
   GBusType bus_type;
   GDBusConnection *connection;
   gchar *object_path;
@@ -142,6 +140,7 @@ struct _GDBusObjectManagerClientPrivate
 
   GDBusProxyTypeFunc get_proxy_type_func;
   gpointer get_proxy_type_user_data;
+  GDestroyNotify get_proxy_type_destroy_notify;
 };
 
 enum
@@ -154,7 +153,8 @@ enum
   PROP_NAME,
   PROP_NAME_OWNER,
   PROP_GET_PROXY_TYPE_FUNC,
-  PROP_GET_PROXY_TYPE_USER_DATA
+  PROP_GET_PROXY_TYPE_USER_DATA,
+  PROP_GET_PROXY_TYPE_DESTROY_NOTIFY
 };
 
 enum
@@ -171,6 +171,7 @@ static void async_initable_iface_init (GAsyncInitableIface *async_initable_iface
 static void dbus_object_manager_interface_init (GDBusObjectManagerIface *iface);
 
 G_DEFINE_TYPE_WITH_CODE (GDBusObjectManagerClient, g_dbus_object_manager_client, G_TYPE_OBJECT,
+                         G_ADD_PRIVATE (GDBusObjectManagerClient)
                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)
                          G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init)
                          G_IMPLEMENT_INTERFACE (G_TYPE_DBUS_OBJECT_MANAGER, dbus_object_manager_interface_init));
@@ -198,25 +199,31 @@ g_dbus_object_manager_client_finalize (GObject *object)
 
   if (manager->priv->control_proxy != NULL)
     {
-      g_warn_if_fail (g_signal_handlers_disconnect_by_func (manager->priv->control_proxy,
-                                                            on_control_proxy_g_signal,
-                                                            manager) == 1);
+      g_signal_handlers_disconnect_by_func (manager->priv->control_proxy,
+                                            on_control_proxy_g_signal,
+                                            manager);
       g_object_unref (manager->priv->control_proxy);
     }
-  g_object_unref (manager->priv->connection);
+  if (manager->priv->connection != NULL)
+    g_object_unref (manager->priv->connection);
   g_free (manager->priv->object_path);
   g_free (manager->priv->name);
   g_free (manager->priv->name_owner);
 
+  if (manager->priv->get_proxy_type_destroy_notify != NULL)
+    manager->priv->get_proxy_type_destroy_notify (manager->priv->get_proxy_type_user_data);
+
+  g_mutex_clear (&manager->priv->lock);
+
   if (G_OBJECT_CLASS (g_dbus_object_manager_client_parent_class)->finalize != NULL)
     G_OBJECT_CLASS (g_dbus_object_manager_client_parent_class)->finalize (object);
 }
 
 static void
 g_dbus_object_manager_client_get_property (GObject    *_object,
-                                    guint       prop_id,
-                                    GValue     *value,
-                                    GParamSpec *pspec)
+                                           guint       prop_id,
+                                           GValue     *value,
+                                           GParamSpec *pspec)
 {
   GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (_object);
 
@@ -243,18 +250,19 @@ g_dbus_object_manager_client_get_property (GObject    *_object,
       break;
 
     default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (_object, prop_id, pspec);
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (manager, prop_id, pspec);
       break;
     }
 }
 
 static void
 g_dbus_object_manager_client_set_property (GObject       *_object,
-                                    guint          prop_id,
-                                    const GValue  *value,
-                                    GParamSpec    *pspec)
+                                           guint          prop_id,
+                                           const GValue  *value,
+                                           GParamSpec    *pspec)
 {
   GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (_object);
+  const gchar *name;
 
   switch (prop_id)
     {
@@ -279,8 +287,9 @@ g_dbus_object_manager_client_set_property (GObject       *_object,
 
     case PROP_NAME:
       g_assert (manager->priv->name == NULL);
-      g_assert (g_dbus_is_name (g_value_get_string (value)));
-      manager->priv->name = g_value_dup_string (value);
+      name = g_value_get_string (value);
+      g_assert (name == NULL || g_dbus_is_name (name));
+      manager->priv->name = g_strdup (name);
       break;
 
     case PROP_FLAGS:
@@ -295,8 +304,12 @@ g_dbus_object_manager_client_set_property (GObject       *_object,
       manager->priv->get_proxy_type_user_data = g_value_get_pointer (value);
       break;
 
+    case PROP_GET_PROXY_TYPE_DESTROY_NOTIFY:
+      manager->priv->get_proxy_type_destroy_notify = g_value_get_pointer (value);
+      break;
+
     default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (_object, prop_id, pspec);
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (manager, prop_id, pspec);
       break;
     }
 }
@@ -462,6 +475,23 @@ g_dbus_object_manager_client_class_init (GDBusObjectManagerClientClass *klass)
                                                          G_PARAM_STATIC_STRINGS));
 
   /**
+   * GDBusObjectManagerClient:get-proxy-type-destroy-notify:
+   *
+   * A #GDestroyNotify for the #gpointer user_data in #GDBusObjectManagerClient:get-proxy-type-user-data.
+   *
+   * Since: 2.30
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_GET_PROXY_TYPE_DESTROY_NOTIFY,
+                                   g_param_spec_pointer ("get-proxy-type-destroy-notify",
+                                                         "GDBusProxyTypeFunc user data free function",
+                                                         "The GDBusProxyTypeFunc user data free function",
+                                                         G_PARAM_READABLE |
+                                                         G_PARAM_WRITABLE |
+                                                         G_PARAM_CONSTRUCT_ONLY |
+                                                         G_PARAM_STATIC_STRINGS));
+
+  /**
    * GDBusObjectManagerClient::interface-proxy-signal:
    * @manager: The #GDBusObjectManagerClient emitting the signal.
    * @object_proxy: The #GDBusObjectProxy on which an interface is emitting a D-Bus signal.
@@ -476,7 +506,7 @@ g_dbus_object_manager_client_class_init (GDBusObjectManagerClientClass *klass)
    * connect signals to all interface proxies managed by @manager.
    *
    * This signal is emitted in the
-   * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+   * [thread-default main context][g-main-context-push-thread-default]
    * that @manager was constructed in.
    *
    * Since: 2.30
@@ -488,7 +518,7 @@ g_dbus_object_manager_client_class_init (GDBusObjectManagerClientClass *klass)
                   G_STRUCT_OFFSET (GDBusObjectManagerClientClass, interface_proxy_signal),
                   NULL,
                   NULL,
-                  _gio_marshal_VOID__OBJECT_OBJECT_STRING_STRING_VARIANT,
+                  NULL,
                   G_TYPE_NONE,
                   5,
                   G_TYPE_DBUS_OBJECT_PROXY,
@@ -514,7 +544,7 @@ g_dbus_object_manager_client_class_init (GDBusObjectManagerClientClass *klass)
    * connect signals to all interface proxies managed by @manager.
    *
    * This signal is emitted in the
-   * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+   * [thread-default main context][g-main-context-push-thread-default]
    * that @manager was constructed in.
    *
    * Since: 2.30
@@ -526,23 +556,20 @@ g_dbus_object_manager_client_class_init (GDBusObjectManagerClientClass *klass)
                   G_STRUCT_OFFSET (GDBusObjectManagerClientClass, interface_proxy_properties_changed),
                   NULL,
                   NULL,
-                  _gio_marshal_VOID__OBJECT_OBJECT_VARIANT_BOXED,
+                  NULL,
                   G_TYPE_NONE,
                   4,
                   G_TYPE_DBUS_OBJECT_PROXY,
                   G_TYPE_DBUS_PROXY,
                   G_TYPE_VARIANT,
                   G_TYPE_STRV);
-
-  g_type_class_add_private (klass, sizeof (GDBusObjectManagerClientPrivate));
 }
 
 static void
 g_dbus_object_manager_client_init (GDBusObjectManagerClient *manager)
 {
-  manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
-                                               G_TYPE_DBUS_OBJECT_MANAGER_CLIENT,
-                                               GDBusObjectManagerClientPrivate);
+  manager->priv = g_dbus_object_manager_client_get_instance_private (manager);
+  g_mutex_init (&manager->priv->lock);
   manager->priv->map_object_path_to_object_proxy = g_hash_table_new_full (g_str_hash,
                                                                           g_str_equal,
                                                                           g_free,
@@ -555,11 +582,12 @@ g_dbus_object_manager_client_init (GDBusObjectManagerClient *manager)
  * g_dbus_object_manager_client_new_sync:
  * @connection: A #GDBusConnection.
  * @flags: Zero or more flags from the #GDBusObjectManagerClientFlags enumeration.
- * @name: The owner of the control object (unique or well-known name).
+ * @name: (allow-none): The owner of the control object (unique or well-known name), or %NULL when not using a message bus connection.
  * @object_path: The object path of the control object.
- * @get_proxy_type_func: A #GDBusProxyTypeFunc function or %NULL to always construct #GDBusProxy proxies.
+ * @get_proxy_type_func: (allow-none): A #GDBusProxyTypeFunc function or %NULL to always construct #GDBusProxy proxies.
  * @get_proxy_type_user_data: User data to pass to @get_proxy_type_func.
- * @cancellable: A #GCancellable or %NULL
+ * @get_proxy_type_destroy_notify: (allow-none): Free function for @get_proxy_type_user_data or %NULL.
+ * @cancellable: (allow-none): A #GCancellable or %NULL
  * @error: Return location for error or %NULL.
  *
  * Creates a new #GDBusObjectManagerClient object.
@@ -581,6 +609,7 @@ g_dbus_object_manager_client_new_sync (GDBusConnection               *connection
                                        const gchar                   *object_path,
                                        GDBusProxyTypeFunc             get_proxy_type_func,
                                        gpointer                       get_proxy_type_user_data,
+                                       GDestroyNotify                 get_proxy_type_destroy_notify,
                                        GCancellable                  *cancellable,
                                        GError                       **error)
 {
@@ -601,6 +630,7 @@ g_dbus_object_manager_client_new_sync (GDBusConnection               *connection
                              "object-path", object_path,
                              "get-proxy-type-func", get_proxy_type_func,
                              "get-proxy-type-user-data", get_proxy_type_user_data,
+                             "get-proxy-type-destroy-notify", get_proxy_type_destroy_notify,
                              NULL);
   if (initable != NULL)
     return G_DBUS_OBJECT_MANAGER (initable);
@@ -614,9 +644,10 @@ g_dbus_object_manager_client_new_sync (GDBusConnection               *connection
  * @flags: Zero or more flags from the #GDBusObjectManagerClientFlags enumeration.
  * @name: The owner of the control object (unique or well-known name).
  * @object_path: The object path of the control object.
- * @get_proxy_type_func: A #GDBusProxyTypeFunc function or %NULL to always construct #GDBusProxy proxies.
+ * @get_proxy_type_func: (allow-none): A #GDBusProxyTypeFunc function or %NULL to always construct #GDBusProxy proxies.
  * @get_proxy_type_user_data: User data to pass to @get_proxy_type_func.
- * @cancellable: A #GCancellable or %NULL
+ * @get_proxy_type_destroy_notify: (allow-none): Free function for @get_proxy_type_user_data or %NULL.
+ * @cancellable: (allow-none): A #GCancellable or %NULL
  * @callback: A #GAsyncReadyCallback to call when the request is satisfied.
  * @user_data: The data to pass to @callback.
  *
@@ -624,7 +655,7 @@ g_dbus_object_manager_client_new_sync (GDBusConnection               *connection
  *
  * This is an asynchronous failable constructor. When the result is
  * ready, @callback will be invoked in the
- * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * [thread-default main context][g-main-context-push-thread-default]
  * of the thread you are calling this method from. You can
  * then call g_dbus_object_manager_client_new_finish() to get the result. See
  * g_dbus_object_manager_client_new_sync() for the synchronous version.
@@ -638,6 +669,7 @@ g_dbus_object_manager_client_new (GDBusConnection               *connection,
                                   const gchar                   *object_path,
                                   GDBusProxyTypeFunc             get_proxy_type_func,
                                   gpointer                       get_proxy_type_user_data,
+                                  GDestroyNotify                 get_proxy_type_destroy_notify,
                                   GCancellable                  *cancellable,
                                   GAsyncReadyCallback            callback,
                                   gpointer                       user_data)
@@ -658,6 +690,7 @@ g_dbus_object_manager_client_new (GDBusConnection               *connection,
                               "object-path", object_path,
                               "get-proxy-type-func", get_proxy_type_func,
                               "get-proxy-type-user-data", get_proxy_type_user_data,
+                              "get-proxy-type-destroy-notify", get_proxy_type_destroy_notify,
                               NULL);
 }
 
@@ -676,7 +709,7 @@ g_dbus_object_manager_client_new (GDBusConnection               *connection,
  */
 GDBusObjectManager *
 g_dbus_object_manager_client_new_finish (GAsyncResult   *res,
-                                 GError        **error)
+                                         GError        **error)
 {
   GObject *object;
   GObject *source_object;
@@ -703,9 +736,10 @@ g_dbus_object_manager_client_new_finish (GAsyncResult   *res,
  * @flags: Zero or more flags from the #GDBusObjectManagerClientFlags enumeration.
  * @name: The owner of the control object (unique or well-known name).
  * @object_path: The object path of the control object.
- * @get_proxy_type_func: A #GDBusProxyTypeFunc function or %NULL to always construct #GDBusProxy proxies.
+ * @get_proxy_type_func: (allow-none): A #GDBusProxyTypeFunc function or %NULL to always construct #GDBusProxy proxies.
  * @get_proxy_type_user_data: User data to pass to @get_proxy_type_func.
- * @cancellable: A #GCancellable or %NULL
+ * @get_proxy_type_destroy_notify: (allow-none): Free function for @get_proxy_type_user_data or %NULL.
+ * @cancellable: (allow-none): A #GCancellable or %NULL
  * @error: Return location for error or %NULL.
  *
  * Like g_dbus_object_manager_client_new_sync() but takes a #GBusType instead
@@ -728,6 +762,7 @@ g_dbus_object_manager_client_new_for_bus_sync (GBusType                       bu
                                                const gchar                   *object_path,
                                                GDBusProxyTypeFunc             get_proxy_type_func,
                                                gpointer                       get_proxy_type_user_data,
+                                               GDestroyNotify                 get_proxy_type_destroy_notify,
                                                GCancellable                  *cancellable,
                                                GError                       **error)
 {
@@ -747,6 +782,7 @@ g_dbus_object_manager_client_new_for_bus_sync (GBusType                       bu
                              "object-path", object_path,
                              "get-proxy-type-func", get_proxy_type_func,
                              "get-proxy-type-user-data", get_proxy_type_user_data,
+                             "get-proxy-type-destroy-notify", get_proxy_type_destroy_notify,
                              NULL);
   if (initable != NULL)
     return G_DBUS_OBJECT_MANAGER (initable);
@@ -760,9 +796,10 @@ g_dbus_object_manager_client_new_for_bus_sync (GBusType                       bu
  * @flags: Zero or more flags from the #GDBusObjectManagerClientFlags enumeration.
  * @name: The owner of the control object (unique or well-known name).
  * @object_path: The object path of the control object.
- * @get_proxy_type_func: A #GDBusProxyTypeFunc function or %NULL to always construct #GDBusProxy proxies.
+ * @get_proxy_type_func: (allow-none): A #GDBusProxyTypeFunc function or %NULL to always construct #GDBusProxy proxies.
  * @get_proxy_type_user_data: User data to pass to @get_proxy_type_func.
- * @cancellable: A #GCancellable or %NULL
+ * @get_proxy_type_destroy_notify: (allow-none): Free function for @get_proxy_type_user_data or %NULL.
+ * @cancellable: (allow-none): A #GCancellable or %NULL
  * @callback: A #GAsyncReadyCallback to call when the request is satisfied.
  * @user_data: The data to pass to @callback.
  *
@@ -771,7 +808,7 @@ g_dbus_object_manager_client_new_for_bus_sync (GBusType                       bu
  *
  * This is an asynchronous failable constructor. When the result is
  * ready, @callback will be invoked in the
- * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * [thread-default main loop][g-main-context-push-thread-default]
  * of the thread you are calling this method from. You can
  * then call g_dbus_object_manager_client_new_for_bus_finish() to get the result. See
  * g_dbus_object_manager_client_new_for_bus_sync() for the synchronous version.
@@ -785,6 +822,7 @@ g_dbus_object_manager_client_new_for_bus (GBusType                       bus_typ
                                           const gchar                   *object_path,
                                           GDBusProxyTypeFunc             get_proxy_type_func,
                                           gpointer                       get_proxy_type_user_data,
+                                          GDestroyNotify                 get_proxy_type_destroy_notify,
                                           GCancellable                  *cancellable,
                                           GAsyncReadyCallback            callback,
                                           gpointer                       user_data)
@@ -804,6 +842,7 @@ g_dbus_object_manager_client_new_for_bus (GBusType                       bus_typ
                               "object-path", object_path,
                               "get-proxy-type-func", get_proxy_type_func,
                               "get-proxy-type-user-data", get_proxy_type_user_data,
+                              "get-proxy-type-destroy-notify", get_proxy_type_destroy_notify,
                               NULL);
 }
 
@@ -857,15 +896,20 @@ g_dbus_object_manager_client_new_for_bus_finish (GAsyncResult   *res,
 GDBusConnection *
 g_dbus_object_manager_client_get_connection (GDBusObjectManagerClient *manager)
 {
+  GDBusConnection *ret;
   g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), NULL);
-  return manager->priv->connection;
+  g_mutex_lock (&manager->priv->lock);
+  ret = manager->priv->connection;
+  g_mutex_unlock (&manager->priv->lock);
+  return ret;
 }
 
 /**
  * g_dbus_object_manager_client_get_name:
  * @manager: A #GDBusObjectManagerClient
  *
- * Gets the name that @manager is for.
+ * Gets the name that @manager is for, or %NULL if not a message bus
+ * connection.
  *
  * Returns: A unique or well-known name. Do not free, the string
  * belongs to @manager.
@@ -875,8 +919,12 @@ g_dbus_object_manager_client_get_connection (GDBusObjectManagerClient *manager)
 const gchar *
 g_dbus_object_manager_client_get_name (GDBusObjectManagerClient *manager)
 {
+  const gchar *ret;
   g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), NULL);
-  return manager->priv->name;
+  g_mutex_lock (&manager->priv->lock);
+  ret = manager->priv->name;
+  g_mutex_unlock (&manager->priv->lock);
+  return ret;
 }
 
 /**
@@ -893,8 +941,12 @@ g_dbus_object_manager_client_get_name (GDBusObjectManagerClient *manager)
 GDBusObjectManagerClientFlags
 g_dbus_object_manager_client_get_flags (GDBusObjectManagerClient *manager)
 {
+  GDBusObjectManagerClientFlags ret;
   g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE);
-  return manager->priv->flags;
+  g_mutex_lock (&manager->priv->lock);
+  ret = manager->priv->flags;
+  g_mutex_unlock (&manager->priv->lock);
+  return ret;
 }
 
 /**
@@ -906,16 +958,20 @@ g_dbus_object_manager_client_get_flags (GDBusObjectManagerClient *manager)
  * #GObject::notify signal to track changes to the
  * #GDBusObjectManagerClient:name-owner property.
  *
- * Returns: The name owner or %NULL if no name owner exists. Free with
- * g_free().
+ * Returns: (nullable): The name owner or %NULL if no name owner
+ * exists. Free with g_free().
  *
  * Since: 2.30
  */
 gchar *
 g_dbus_object_manager_client_get_name_owner (GDBusObjectManagerClient *manager)
 {
+  gchar *ret;
   g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), NULL);
-  return g_strdup (manager->priv->name_owner);
+  g_mutex_lock (&manager->priv->lock);
+  ret = g_strdup (manager->priv->name_owner);
+  g_mutex_unlock (&manager->priv->lock);
+  return ret;
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
@@ -936,12 +992,19 @@ signal_cb (GDBusConnection *connection,
   GDBusObjectProxy *object_proxy;
   GDBusInterface *interface;
 
+  g_mutex_lock (&manager->priv->lock);
   object_proxy = g_hash_table_lookup (manager->priv->map_object_path_to_object_proxy, object_path);
   if (object_proxy == NULL)
-    goto out;
+    {
+      g_mutex_unlock (&manager->priv->lock);
+      goto out;
+    }
+  g_object_ref (object_proxy);
+  g_mutex_unlock (&manager->priv->lock);
 
   //g_debug ("yay, signal_cb %s %s: %s\n", signal_name, object_path, g_variant_print (parameters, TRUE));
 
+  g_object_ref (manager);
   if (g_strcmp0 (interface_name, "org.freedesktop.DBus.Properties") == 0)
     {
       if (g_strcmp0 (signal_name, "PropertiesChanged") == 0)
@@ -984,6 +1047,10 @@ signal_cb (GDBusConnection *connection,
                                                     NULL);
                 }
               /* ... and then synthesize the signal */
+              g_signal_emit_by_name (interface,
+                                     "g-properties-changed",
+                                     changed_properties,
+                                     invalidated_properties);
               g_signal_emit (manager,
                              signals[INTERFACE_PROXY_PROPERTIES_CHANGED_SIGNAL],
                              0,
@@ -991,10 +1058,6 @@ signal_cb (GDBusConnection *connection,
                              interface,
                              changed_properties,
                              invalidated_properties);
-              g_signal_emit_by_name (interface,
-                                     "g-properties-changed",
-                                     changed_properties,
-                                     invalidated_properties);
               g_object_unref (interface);
             }
           g_variant_unref (changed_properties);
@@ -1007,6 +1070,11 @@ signal_cb (GDBusConnection *connection,
       interface = g_dbus_object_get_interface (G_DBUS_OBJECT (object_proxy), interface_name);
       if (interface != NULL)
         {
+          g_signal_emit_by_name (interface,
+                                 "g-signal",
+                                 sender_name,
+                                 signal_name,
+                                 parameters);
           g_signal_emit (manager,
                          signals[INTERFACE_PROXY_SIGNAL_SIGNAL],
                          0,
@@ -1015,55 +1083,66 @@ signal_cb (GDBusConnection *connection,
                          sender_name,
                          signal_name,
                          parameters);
-          g_signal_emit_by_name (interface,
-                                 "g-signal",
-                                 sender_name,
-                                 signal_name,
-                                 parameters);
           g_object_unref (interface);
         }
     }
+  g_object_unref (manager);
 
  out:
-  ;
+  g_clear_object (&object_proxy);
 }
 
 static void
 subscribe_signals (GDBusObjectManagerClient *manager,
                    const gchar *name_owner)
 {
-  GError *error;
+  GError *error = NULL;
   GVariant *ret;
 
   g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager));
   g_return_if_fail (manager->priv->signal_subscription_id == 0);
-  g_return_if_fail (g_dbus_is_unique_name (name_owner));
+  g_return_if_fail (name_owner == NULL || g_dbus_is_unique_name (name_owner));
 
-  /* the bus daemon may not implement path_prefix so gracefully
-   * handle this by using a fallback
-   */
-  manager->priv->match_rule = g_strdup_printf ("type='signal',sender='%s',path_namespace='%s'",
-                                               name_owner,
-                                               manager->priv->object_path);
-
-  error = NULL;
-  ret = g_dbus_connection_call_sync (manager->priv->connection,
-                                     "org.freedesktop.DBus",
-                                     "/org/freedeskop/DBus",
-                                     "org.freedesktop.DBus",
-                                     "AddMatch",
-                                     g_variant_new ("(s)",
-                                                    manager->priv->match_rule),
-                                     NULL, /* reply_type */
-                                     G_DBUS_CALL_FLAGS_NONE,
-                                     -1, /* default timeout */
-                                     NULL, /* TODO: Cancellable */
-                                     &error);
-  if (ret != NULL)
+  if (name_owner != NULL)
     {
+      /* Only add path_namespace if it's non-'/'. This removes a no-op key from
+       * the match rule, and also works around a D-Bus bug where
+       * path_namespace='/' matches nothing in D-Bus versions < 1.6.18.
+       *
+       * See: https://bugs.freedesktop.org/show_bug.cgi?id=70799 */
+      if (g_str_equal (manager->priv->object_path, "/"))
+        {
+          manager->priv->match_rule = g_strdup_printf ("type='signal',sender='%s'",
+                                                       name_owner);
+        }
+      else
+        {
+          manager->priv->match_rule = g_strdup_printf ("type='signal',sender='%s',path_namespace='%s'",
+                                                       name_owner, manager->priv->object_path);
+        }
+
+      /* The bus daemon may not implement path_namespace so gracefully
+       * handle this by using a fallback triggered if @error is set. */
+      ret = g_dbus_connection_call_sync (manager->priv->connection,
+                                         "org.freedesktop.DBus",
+                                         "/org/freedesktop/DBus",
+                                         "org.freedesktop.DBus",
+                                         "AddMatch",
+                                         g_variant_new ("(s)",
+                                                        manager->priv->match_rule),
+                                         NULL, /* reply_type */
+                                         G_DBUS_CALL_FLAGS_NONE,
+                                         -1, /* default timeout */
+                                         NULL, /* TODO: Cancellable */
+                                         &error);
+
       /* yay, bus daemon supports path_namespace */
-      g_variant_unref (ret);
+      if (ret != NULL)
+        g_variant_unref (ret);
+    }
 
+  if (error == NULL)
+    {
       /* still need to ask GDBusConnection for the callbacks */
       manager->priv->signal_subscription_id =
         g_dbus_connection_signal_subscribe (manager->priv->connection,
@@ -1131,7 +1210,7 @@ maybe_unsubscribe_signals (GDBusObjectManagerClient *manager)
        */
       g_dbus_connection_call (manager->priv->connection,
                               "org.freedesktop.DBus",
-                              "/org/freedeskop/DBus",
+                              "/org/freedesktop/DBus",
                               "org.freedesktop.DBus",
                               "RemoveMatch",
                               g_variant_new ("(s)",
@@ -1159,36 +1238,44 @@ on_notify_g_name_owner (GObject    *object,
   gchar *old_name_owner;
   gchar *new_name_owner;
 
+  g_mutex_lock (&manager->priv->lock);
   old_name_owner = manager->priv->name_owner;
   new_name_owner = g_dbus_proxy_get_name_owner (manager->priv->control_proxy);
   manager->priv->name_owner = NULL;
 
+  g_object_ref (manager);
   if (g_strcmp0 (old_name_owner, new_name_owner) != 0)
     {
       GList *l;
       GList *proxies;
 
+      /* remote manager changed; nuke all local proxies  */
+      proxies = g_hash_table_get_values (manager->priv->map_object_path_to_object_proxy);
+      g_list_foreach (proxies, (GFunc) g_object_ref, NULL);
+      g_hash_table_remove_all (manager->priv->map_object_path_to_object_proxy);
+
+      g_mutex_unlock (&manager->priv->lock);
+
       /* do the :name-owner notify with a NULL name - this way the user knows
        * the ::object-proxy-removed following is because the name owner went
        * away
        */
       g_object_notify (G_OBJECT (manager), "name-owner");
 
-      /* remote manager changed; nuke all local proxies  */
-      proxies = g_hash_table_get_values (manager->priv->map_object_path_to_object_proxy);
-      g_list_foreach (proxies, (GFunc) g_object_ref, NULL);
-      g_hash_table_remove_all (manager->priv->map_object_path_to_object_proxy);
       for (l = proxies; l != NULL; l = l->next)
         {
           GDBusObjectProxy *object_proxy = G_DBUS_OBJECT_PROXY (l->data);
           g_signal_emit_by_name (manager, "object-removed", object_proxy);
         }
-      g_list_foreach (proxies, (GFunc) g_object_unref, NULL);
-      g_list_free (proxies);
+      g_list_free_full (proxies, g_object_unref);
 
       /* nuke local filter */
       maybe_unsubscribe_signals (manager);
     }
+  else
+    {
+      g_mutex_unlock (&manager->priv->lock);
+    }
 
   if (new_name_owner != NULL)
     {
@@ -1226,11 +1313,14 @@ on_notify_g_name_owner (GObject    *object,
       /* do the :name-owner notify *AFTER* emitting ::object-proxy-added signals - this
        * way the user knows that the signals were emitted because the name owner came back
        */
+      g_mutex_lock (&manager->priv->lock);
       manager->priv->name_owner = new_name_owner;
+      g_mutex_unlock (&manager->priv->lock);
       g_object_notify (G_OBJECT (manager), "name-owner");
 
     }
   g_free (old_name_owner);
+  g_object_unref (manager);
 }
 
 static gboolean
@@ -1255,7 +1345,7 @@ initable_init (GInitable     *initable,
 
   proxy_flags = G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES;
   if (manager->priv->flags & G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START)
-    proxy_flags |= G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START;;
+    proxy_flags |= G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START;
 
   manager->priv->control_proxy = g_dbus_proxy_new_sync (manager->priv->connection,
                                                         proxy_flags,
@@ -1273,8 +1363,13 @@ initable_init (GInitable     *initable,
                     G_CALLBACK (on_notify_g_name_owner),
                     manager);
 
+  g_signal_connect (manager->priv->control_proxy,
+                    "g-signal",
+                    G_CALLBACK (on_control_proxy_g_signal),
+                    manager);
+
   manager->priv->name_owner = g_dbus_proxy_get_name_owner (manager->priv->control_proxy);
-  if (manager->priv->name_owner == NULL)
+  if (manager->priv->name_owner == NULL && manager->priv->name != NULL)
     {
       /* it's perfectly fine if there's no name owner.. we're just going to
        * wait until one is ready
@@ -1282,11 +1377,7 @@ initable_init (GInitable     *initable,
     }
   else
     {
-      /* yay, we have a name owner */
-      g_signal_connect (manager->priv->control_proxy,
-                        "g-signal",
-                        G_CALLBACK (on_control_proxy_g_signal),
-                        manager);
+      /* yay, we can get the objects */
       subscribe_signals (manager,
                          manager->priv->name_owner);
       value = g_dbus_proxy_call_sync (manager->priv->control_proxy,
@@ -1342,16 +1433,39 @@ add_interfaces (GDBusObjectManagerClient *manager,
   GVariantIter iter;
   const gchar *interface_name;
   GVariant *properties;
+  GList *interface_added_signals, *l;
+  GDBusProxy *interface_proxy;
 
-  g_return_if_fail (g_dbus_is_unique_name (name_owner));
+  g_return_if_fail (name_owner == NULL || g_dbus_is_unique_name (name_owner));
 
+  g_mutex_lock (&manager->priv->lock);
+
+  interface_added_signals = NULL;
   added = FALSE;
+
   op = g_hash_table_lookup (manager->priv->map_object_path_to_object_proxy, object_path);
   if (op == NULL)
     {
-      op = _g_dbus_object_proxy_new (manager->priv->connection, object_path);
+      GType object_proxy_type;
+      if (manager->priv->get_proxy_type_func != NULL)
+        {
+          object_proxy_type = manager->priv->get_proxy_type_func (manager,
+                                                                  object_path,
+                                                                  NULL,
+                                                                  manager->priv->get_proxy_type_user_data);
+          g_warn_if_fail (g_type_is_a (object_proxy_type, G_TYPE_DBUS_OBJECT_PROXY));
+        }
+      else
+        {
+          object_proxy_type = G_TYPE_DBUS_OBJECT_PROXY;
+        }
+      op = g_object_new (object_proxy_type,
+                         "g-connection", manager->priv->connection,
+                         "g-object-path", object_path,
+                         NULL);
       added = TRUE;
     }
+  g_object_ref (op);
 
   g_variant_iter_init (&iter, ifaces_and_properties);
   while (g_variant_iter_next (&iter,
@@ -1359,7 +1473,6 @@ add_interfaces (GDBusObjectManagerClient *manager,
                               &interface_name,
                               &properties))
     {
-      GDBusProxy *interface_proxy;
       GError *error;
       GType interface_proxy_type;
 
@@ -1423,12 +1536,24 @@ add_interfaces (GDBusObjectManagerClient *manager,
 
           _g_dbus_object_proxy_add_interface (op, interface_proxy);
           if (!added)
-            g_signal_emit_by_name (manager, "interface-added", op, interface_proxy);
+            interface_added_signals = g_list_append (interface_added_signals, g_object_ref (interface_proxy));
           g_object_unref (interface_proxy);
         }
       g_variant_unref (properties);
     }
 
+  g_mutex_unlock (&manager->priv->lock);
+
+  /* now that we don't hold the lock any more, emit signals */
+  g_object_ref (manager);
+  for (l = interface_added_signals; l != NULL; l = l->next)
+    {
+      interface_proxy = G_DBUS_PROXY (l->data);
+      g_signal_emit_by_name (manager, "interface-added", op, interface_proxy);
+      g_object_unref (interface_proxy);
+    }
+  g_list_free (interface_added_signals);
+
   if (added)
     {
       g_hash_table_insert (manager->priv->map_object_path_to_object_proxy,
@@ -1436,6 +1561,8 @@ add_interfaces (GDBusObjectManagerClient *manager,
                            op);
       g_signal_emit_by_name (manager, "object-added", op);
     }
+  g_object_unref (manager);
+  g_object_unref (op);
 }
 
 static void
@@ -1449,32 +1576,38 @@ remove_interfaces (GDBusObjectManagerClient   *manager,
   guint num_interfaces;
   guint num_interfaces_to_remove;
 
+  g_mutex_lock (&manager->priv->lock);
+
   op = g_hash_table_lookup (manager->priv->map_object_path_to_object_proxy, object_path);
   if (op == NULL)
     {
       g_warning ("%s: Processing InterfaceRemoved signal for path %s but no object proxy exists",
                  G_STRLOC,
                  object_path);
+      g_mutex_unlock (&manager->priv->lock);
       goto out;
     }
 
   interfaces = g_dbus_object_get_interfaces (G_DBUS_OBJECT (op));
   num_interfaces = g_list_length (interfaces);
-  g_list_foreach (interfaces, (GFunc) g_object_unref, NULL);
-  g_list_free (interfaces);
+  g_list_free_full (interfaces, g_object_unref);
 
   num_interfaces_to_remove = g_strv_length ((gchar **) interface_names);
 
   /* see if we are going to completety remove the object */
+  g_object_ref (manager);
   if (num_interfaces_to_remove == num_interfaces)
     {
       g_object_ref (op);
       g_warn_if_fail (g_hash_table_remove (manager->priv->map_object_path_to_object_proxy, object_path));
+      g_mutex_unlock (&manager->priv->lock);
       g_signal_emit_by_name (manager, "object-removed", op);
       g_object_unref (op);
     }
   else
     {
+      g_object_ref (op);
+      g_mutex_unlock (&manager->priv->lock);
       for (n = 0; interface_names != NULL && interface_names[n] != NULL; n++)
         {
           GDBusInterface *interface;
@@ -1486,7 +1619,9 @@ remove_interfaces (GDBusObjectManagerClient   *manager,
               g_object_unref (interface);
             }
         }
+      g_object_unref (op);
     }
+  g_object_unref (manager);
  out:
   ;
 }
@@ -1501,7 +1636,7 @@ process_get_all_result (GDBusObjectManagerClient *manager,
   GVariant *ifaces_and_properties;
   GVariantIter iter;
 
-  g_return_if_fail (g_dbus_is_unique_name (name_owner));
+  g_return_if_fail (name_owner == NULL || g_dbus_is_unique_name (name_owner));
 
   arg0 = g_variant_get_child_value (value, 0);
   g_variant_iter_init (&iter, arg0);
@@ -1566,9 +1701,11 @@ g_dbus_object_manager_client_get_object (GDBusObjectManager *_manager,
   GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (_manager);
   GDBusObject *ret;
 
+  g_mutex_lock (&manager->priv->lock);
   ret = g_hash_table_lookup (manager->priv->map_object_path_to_object_proxy, object_path);
   if (ret != NULL)
     g_object_ref (ret);
+  g_mutex_unlock (&manager->priv->lock);
   return ret;
 }
 
@@ -1601,8 +1738,11 @@ g_dbus_object_manager_client_get_objects (GDBusObjectManager *_manager)
 
   g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), NULL);
 
+  g_mutex_lock (&manager->priv->lock);
   ret = g_hash_table_get_values (manager->priv->map_object_path_to_object_proxy);
   g_list_foreach (ret, (GFunc) g_object_ref, NULL);
+  g_mutex_unlock (&manager->priv->lock);
+
   return ret;
 }