X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gio%2Fgdbusinterfaceskeleton.c;h=0070e8b6c7d3a81ea07b8ad738d697d9d19e6cb2;hb=33b9935efc82f8cc4747dfea2743129dfc418d19;hp=6c7ba7dfd2795a34013f2d91fe78aa91743563b0;hpb=8f5e0cd24016cdf852a809dfb7b1356328542201;p=platform%2Fupstream%2Fglib.git diff --git a/gio/gdbusinterfaceskeleton.c b/gio/gdbusinterfaceskeleton.c index 6c7ba7d..0070e8b 100644 --- a/gio/gdbusinterfaceskeleton.c +++ b/gio/gdbusinterfaceskeleton.c @@ -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 . * * Author: David Zeuthen */ @@ -29,7 +27,7 @@ #include "gdbusprivate.h" #include "gdbusmethodinvocation.h" #include "gdbusconnection.h" -#include "gioscheduler.h" +#include "gtask.h" #include "gioerror.h" #include "glibintl.h" @@ -44,15 +42,22 @@ struct _GDBusInterfaceSkeletonPrivate { - GDBusObject *object; + GMutex lock; + + 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, @@ -67,25 +72,52 @@ 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)); + G_ADD_PRIVATE (GDBusInterfaceSkeleton) + G_IMPLEMENT_INTERFACE (G_TYPE_DBUS_INTERFACE, dbus_interface_interface_init)) 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); + + g_mutex_clear (&interface->priv->lock); + G_OBJECT_CLASS (g_dbus_interface_skeleton_parent_class)->finalize (object); } @@ -176,11 +208,11 @@ g_dbus_interface_skeleton_class_init (GDBusInterfaceSkeletonClass *klass) * * Note that this signal is emitted in a thread dedicated to * handling the method call so handlers are allowed to perform - * blocking IO. This means that it is appropriate to call - * e.g. polkit_authority_check_authorization_sync() - * with the POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION flag set. + * blocking IO. This means that it is appropriate to call e.g. + * [polkit_authority_check_authorization_sync()](http://hal.freedesktop.org/docs/polkit/PolkitAuthority.html#polkit-authority-check-authorization-sync) + * with the + * [POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION](http://hal.freedesktop.org/docs/polkit/PolkitAuthority.html#POLKIT-CHECK-AUTHORIZATION-FLAGS-ALLOW-USER-INTERACTION:CAPS) + * flag set. * * If %FALSE is returned then no further handlers are run and the * signal handler must take a reference to @invocation and finish @@ -216,18 +248,17 @@ g_dbus_interface_skeleton_class_init (GDBusInterfaceSkeletonClass *klass) G_STRUCT_OFFSET (GDBusInterfaceSkeletonClass, g_authorize_method), _g_signal_accumulator_false_handled, NULL, - g_cclosure_marshal_generic, + NULL, G_TYPE_BOOLEAN, 1, G_TYPE_DBUS_METHOD_INVOCATION); - - g_type_class_add_private (klass, sizeof (GDBusInterfaceSkeletonPrivate)); } static void g_dbus_interface_skeleton_init (GDBusInterfaceSkeleton *interface) { - interface->priv = G_TYPE_INSTANCE_GET_PRIVATE (interface, G_TYPE_DBUS_INTERFACE_SKELETON, GDBusInterfaceSkeletonPrivate); + interface->priv = g_dbus_interface_skeleton_get_instance_private (interface); + g_mutex_init (&interface->priv->lock); } /* ---------------------------------------------------------------------------------------------------- */ @@ -264,11 +295,17 @@ g_dbus_interface_skeleton_set_flags (GDBusInterfaceSkeleton *interface_, GDBusInterfaceSkeletonFlags flags) { g_return_if_fail (G_IS_DBUS_INTERFACE_SKELETON (interface_)); + g_mutex_lock (&interface_->priv->lock); if (interface_->priv->flags != flags) { interface_->priv->flags = flags; + g_mutex_unlock (&interface_->priv->lock); g_object_notify (G_OBJECT (interface_), "g-flags"); } + else + { + g_mutex_unlock (&interface_->priv->lock); + } } /** @@ -320,7 +357,9 @@ g_dbus_interface_skeleton_get_vtable (GDBusInterfaceSkeleton *interface_) * * Gets all D-Bus properties for @interface_. * - * Returns: (transfer full): A #GVariant of type 'a{sv}'. Free with g_variant_unref(). + * Returns: (transfer full): A #GVariant of type + * ['a{sv}'][G-VARIANT-TYPE-VARDICT:CAPS]. + * Free with g_variant_unref(). * * Since: 2.30 */ @@ -342,7 +381,7 @@ g_dbus_interface_skeleton_get_properties (GDBusInterfaceSkeleton *interface_) * * For example, an exported D-Bus interface may queue up property * changes and emit the - * org.freedesktop.DBus.Properties::PropertiesChanged + * `org.freedesktop.DBus.Properties::Propert`` * signal later (e.g. in an idle handler). This technique is useful * for collapsing multiple property changes into one. * @@ -368,7 +407,24 @@ static GDBusObject * g_dbus_interface_skeleton_get_object (GDBusInterface *interface_) { GDBusInterfaceSkeleton *interface = G_DBUS_INTERFACE_SKELETON (interface_); - return interface->priv->object; + GDBusObject *ret; + g_mutex_lock (&interface->priv->lock); + ret = interface->priv->object; + g_mutex_unlock (&interface->priv->lock); + return ret; +} + +static GDBusObject * +g_dbus_interface_skeleton_dup_object (GDBusInterface *interface_) +{ + GDBusInterfaceSkeleton *interface = G_DBUS_INTERFACE_SKELETON (interface_); + GDBusObject *ret; + g_mutex_lock (&interface->priv->lock); + ret = interface->priv->object; + if (ret != NULL) + g_object_ref (ret); + g_mutex_unlock (&interface->priv->lock); + return ret; } static void @@ -376,11 +432,13 @@ g_dbus_interface_skeleton_set_object (GDBusInterface *interface_, GDBusObject *object) { GDBusInterfaceSkeleton *interface = G_DBUS_INTERFACE_SKELETON (interface_); + g_mutex_lock (&interface->priv->lock); if (interface->priv->object != NULL) g_object_remove_weak_pointer (G_OBJECT (interface->priv->object), (gpointer *) &interface->priv->object); interface->priv->object = object; if (object != NULL) g_object_add_weak_pointer (G_OBJECT (interface->priv->object), (gpointer *) &interface->priv->object); + g_mutex_unlock (&interface->priv->lock); } static void @@ -388,6 +446,7 @@ dbus_interface_interface_init (GDBusInterfaceIface *iface) { iface->get_info = _g_dbus_interface_skeleton_get_info; iface->get_object = g_dbus_interface_skeleton_get_object; + iface->dup_object = g_dbus_interface_skeleton_dup_object; iface->set_object = g_dbus_interface_skeleton_set_object; } @@ -399,18 +458,13 @@ typedef struct GDBusInterfaceSkeleton *interface; GDBusInterfaceMethodCallFunc method_call_func; GDBusMethodInvocation *invocation; - GMainContext *context; } DispatchData; static void dispatch_data_unref (DispatchData *data) { if (g_atomic_int_dec_and_test (&data->ref_count)) - { - if (data->context != NULL) - g_main_context_unref (data->context); - g_free (data); - } + g_slice_free (DispatchData, data); } static DispatchData * @@ -435,19 +489,29 @@ dispatch_invoke_in_context_func (gpointer user_data) return FALSE; } -static gboolean -dispatch_in_thread_func (GIOSchedulerJob *job, - GCancellable *cancellable, - gpointer user_data) +static void +dispatch_in_thread_func (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) { - DispatchData *data = user_data; + DispatchData *data = task_data; + GDBusInterfaceSkeletonFlags flags; + GDBusObject *object; gboolean authorized; + g_mutex_lock (&data->interface->priv->lock); + flags = data->interface->priv->flags; + object = data->interface->priv->object; + if (object != NULL) + g_object_ref (object); + g_mutex_unlock (&data->interface->priv->lock); + /* first check on the enclosing object (if any), then the interface */ authorized = TRUE; - if (data->interface->priv->object != NULL) + if (object != NULL) { - g_signal_emit_by_name (data->interface->priv->object, + g_signal_emit_by_name (object, "authorize-method", data->interface, data->invocation, @@ -465,7 +529,7 @@ dispatch_in_thread_func (GIOSchedulerJob *job, if (authorized) { gboolean run_in_thread; - run_in_thread = (data->interface->priv->flags & G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD); + run_in_thread = (flags & G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD); if (run_in_thread) { /* might as well just re-use the existing thread */ @@ -481,8 +545,8 @@ dispatch_in_thread_func (GIOSchedulerJob *job, else { /* bah, back to original context */ - g_main_context_invoke_full (data->context, - G_PRIORITY_DEFAULT, + g_main_context_invoke_full (g_task_get_context (task), + g_task_get_priority (task), dispatch_invoke_in_context_func, dispatch_data_ref (data), (GDestroyNotify) dispatch_data_unref); @@ -493,7 +557,8 @@ dispatch_in_thread_func (GIOSchedulerJob *job, /* do nothing */ } - return FALSE; + if (object != NULL) + g_object_unref (object); } static void @@ -505,11 +570,20 @@ g_dbus_interface_method_dispatch_helper (GDBusInterfaceSkeleton *interface gboolean has_default_class_handler; gboolean emit_authorized_signal; gboolean run_in_thread; + GDBusInterfaceSkeletonFlags flags; + GDBusObject *object; g_return_if_fail (G_IS_DBUS_INTERFACE_SKELETON (interface)); g_return_if_fail (method_call_func != NULL); g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation)); + g_mutex_lock (&interface->priv->lock); + flags = interface->priv->flags; + object = interface->priv->object; + if (object != NULL) + g_object_ref (object); + g_mutex_unlock (&interface->priv->lock); + /* optimization for the common case where * * a) no handler is connected and class handler is not overridden (both interface and object); and @@ -525,11 +599,11 @@ g_dbus_interface_method_dispatch_helper (GDBusInterfaceSkeleton *interface emit_authorized_signal = (has_handlers || !has_default_class_handler); if (!emit_authorized_signal) { - if (interface->priv->object != NULL) - emit_authorized_signal = _g_dbus_object_skeleton_has_authorize_method_handlers (G_DBUS_OBJECT_SKELETON (interface->priv->object)); + if (object != NULL) + emit_authorized_signal = _g_dbus_object_skeleton_has_authorize_method_handlers (G_DBUS_OBJECT_SKELETON (object)); } - run_in_thread = (interface->priv->flags & G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD); + run_in_thread = (flags & G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD); if (!emit_authorized_signal && !run_in_thread) { method_call_func (g_dbus_method_invocation_get_connection (invocation), @@ -543,21 +617,23 @@ g_dbus_interface_method_dispatch_helper (GDBusInterfaceSkeleton *interface } else { + GTask *task; DispatchData *data; - data = g_new0 (DispatchData, 1); + + data = g_slice_new0 (DispatchData); data->interface = interface; data->method_call_func = method_call_func; data->invocation = invocation; - data->context = g_main_context_get_thread_default (); data->ref_count = 1; - if (data->context != NULL) - g_main_context_ref (data->context); - g_io_scheduler_push_job (dispatch_in_thread_func, - data, - (GDestroyNotify) dispatch_data_unref, - G_PRIORITY_DEFAULT, - NULL); /* GCancellable* */ + + task = g_task_new (interface, NULL, NULL, NULL); + g_task_set_task_data (task, data, (GDestroyNotify) dispatch_data_unref); + g_task_run_in_thread (task, dispatch_in_thread_func); + g_object_unref (task); } + + if (object != NULL) + g_object_unref (object); } static void @@ -578,11 +654,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_. @@ -592,8 +769,99 @@ 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 = 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); - return interface_->priv->connection; + + 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 exported 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; } /** @@ -610,8 +878,12 @@ g_dbus_interface_skeleton_get_connection (GDBusInterfaceSkeleton *interface_) const gchar * g_dbus_interface_skeleton_get_object_path (GDBusInterfaceSkeleton *interface_) { + const gchar *ret; g_return_val_if_fail (G_IS_DBUS_INTERFACE_SKELETON (interface_), NULL); - return interface_->priv->object_path; + g_mutex_lock (&interface_->priv->lock); + ret = interface_->priv->object_path; + g_mutex_unlock (&interface_->priv->lock); + return ret; } /** @@ -623,9 +895,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 @@ -636,49 +912,26 @@ 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); - 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; - } + /* 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); - g_assert (interface_->priv->connection == NULL); - g_assert (interface_->priv->object_path == NULL); - g_assert (interface_->priv->hooked_vtable == NULL); + g_mutex_lock (&interface_->priv->lock); - /* 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; + /* Set the object path */ + set_object_path_locked (interface_, object_path); - out: + /* Add the connection */ + ret = add_connection_locked (interface_, connection, error); + + g_mutex_unlock (&interface_->priv->lock); return ret; } @@ -686,8 +939,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_unexport_from_connection() * * Since: 2.30 */ @@ -695,21 +950,59 @@ 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); + + /* Reset the object path if we removed the last connection */ + if (interface_->priv->connections == NULL) + set_object_path_locked (interface_, NULL); - 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; + g_mutex_unlock (&interface_->priv->lock); } /* ---------------------------------------------------------------------------------------------------- */