gdbus: Group interface changes to reduce the amount of signals emitted
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Thu, 4 Oct 2012 07:26:33 +0000 (04:26 -0300)
committerMarcel Holtmann <marcel@holtmann.org>
Mon, 26 Nov 2012 11:54:45 +0000 (12:54 +0100)
InterfacesAdded and InterfacesRemoved can group all the interfaces
changes together in one message.

gdbus/object.c

index 214968e..fef6635 100644 (file)
@@ -45,6 +45,9 @@ struct generic_data {
        char *path;
        GSList *interfaces;
        GSList *objects;
+       GSList *added;
+       GSList *removed;
+       guint process_id;
        char *introspect;
        struct generic_data *parent;
 };
@@ -70,6 +73,8 @@ struct property_data {
        DBusMessage *message;
 };
 
+static gboolean process_changes(gpointer user_data);
+
 static void print_arguments(GString *gstr, const GDBusArgInfo *args,
                                                const char *direction)
 {
@@ -491,21 +496,71 @@ static void reset_parent(gpointer data, gpointer user_data)
        child->parent = parent;
 }
 
-static void generic_unregister(DBusConnection *connection, void *user_data)
+static void append_properties(struct interface_data *data,
+                                                       DBusMessageIter *iter)
 {
-       struct generic_data *data = user_data;
+       DBusMessageIter dict;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                               DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                               DBUS_TYPE_STRING_AS_STRING
+                               DBUS_TYPE_VARIANT_AS_STRING
+                               DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       /* TODO: list properties */
+
+       dbus_message_iter_close_container(iter, &dict);
+}
+
+static void append_interface(gpointer data, gpointer user_data)
+{
+       struct interface_data *iface = data;
+       DBusMessageIter *array = user_data;
+       DBusMessageIter entry;
+
+       dbus_message_iter_open_container(array, DBUS_TYPE_DICT_ENTRY, NULL,
+                                                               &entry);
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &iface->name);
+       append_properties(data, &entry);
+       dbus_message_iter_close_container(array, &entry);
+}
+
+static void emit_interfaces_added(struct generic_data *data)
+{
+       DBusMessage *signal;
+       DBusMessageIter iter, array;
        struct generic_data *parent = data->parent;
 
-       if (parent != NULL)
-               parent->objects = g_slist_remove(parent->objects, data);
+       if (parent == NULL)
+               return;
 
-       g_slist_foreach(data->objects, reset_parent, data->parent);
-       g_slist_free(data->objects);
+       signal = dbus_message_new_signal(parent->path,
+                                       DBUS_INTERFACE_OBJECT_MANAGER,
+                                       "InterfacesAdded");
+       if (signal == NULL)
+               return;
 
-       dbus_connection_unref(data->conn);
-       g_free(data->introspect);
-       g_free(data->path);
-       g_free(data);
+       dbus_message_iter_init_append(signal, &iter);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+                                                               &data->path);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                               DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                               DBUS_TYPE_STRING_AS_STRING
+                               DBUS_TYPE_ARRAY_AS_STRING
+                               DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                               DBUS_TYPE_STRING_AS_STRING
+                               DBUS_TYPE_VARIANT_AS_STRING
+                               DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+                               DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array);
+
+       g_slist_foreach(data->added, append_interface, &array);
+       g_slist_free(data->added);
+       data->added = NULL;
+
+       dbus_message_iter_close_container(&iter, &array);
+
+       g_dbus_send_message(data->conn, signal);
 }
 
 static struct interface_data *find_interface(GSList *interfaces,
@@ -546,45 +601,37 @@ static gboolean g_dbus_args_have_signature(const GDBusArgInfo *args,
        return TRUE;
 }
 
-static DBusHandlerResult generic_message(DBusConnection *connection,
-                                       DBusMessage *message, void *user_data)
+static gboolean remove_interface(struct generic_data *data, const char *name)
 {
-       struct generic_data *data = user_data;
        struct interface_data *iface;
-       const GDBusMethodTable *method;
-       const char *interface;
 
-       interface = dbus_message_get_interface(message);
-
-       iface = find_interface(data->interfaces, interface);
+       iface = find_interface(data->interfaces, name);
        if (iface == NULL)
-               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-
-       for (method = iface->methods; method &&
-                       method->name && method->function; method++) {
-               if (dbus_message_is_method_call(message, iface->name,
-                                                       method->name) == FALSE)
-                       continue;
+               return FALSE;
 
-               if (g_dbus_args_have_signature(method->in_args,
-                                                       message) == FALSE)
-                       continue;
+       data->interfaces = g_slist_remove(data->interfaces, iface);
 
-               if (check_privilege(connection, message, method,
-                                               iface->user_data) == TRUE)
-                       return DBUS_HANDLER_RESULT_HANDLED;
+       if (iface->destroy) {
+               iface->destroy(iface->user_data);
+               iface->user_data = NULL;
+       }
 
-               return process_message(connection, message, method,
-                                                       iface->user_data);
+       if (data->parent == NULL) {
+               g_free(iface->name);
+               g_free(iface);
+               return TRUE;
        }
 
-       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-}
+       data->removed = g_slist_prepend(data->removed, iface->name);
+       g_free(iface);
 
-static DBusObjectPathVTable generic_table = {
-       .unregister_function    = generic_unregister,
-       .message_function       = generic_message,
-};
+       if (data->process_id > 0)
+               return TRUE;
+
+       data->process_id = g_idle_add(process_changes, data);
+
+       return TRUE;
+}
 
 static struct generic_data *invalidate_parent_data(DBusConnection *conn,
                                                const char *child_path)
@@ -636,12 +683,6 @@ done:
        return data;
 }
 
-static const GDBusMethodTable introspect_methods[] = {
-       { GDBUS_METHOD("Introspect", NULL,
-                       GDBUS_ARGS({ "xml", "s" }), introspect) },
-       { }
-};
-
 static inline const GDBusPropertyTable *find_property(const GDBusPropertyTable *properties,
                                                        const char *name)
 {
@@ -860,35 +901,128 @@ static const GDBusSignalTable properties_signals[] = {
        { }
 };
 
-static void append_properties(struct interface_data *data,
-                                                       DBusMessageIter *iter)
+static void append_name(gpointer data, gpointer user_data)
 {
-       DBusMessageIter dict;
+       char *name = data;
+       DBusMessageIter *iter = user_data;
 
-       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
-                               DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
-                               DBUS_TYPE_STRING_AS_STRING
-                               DBUS_TYPE_VARIANT_AS_STRING
-                               DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &name);
+}
 
-       /* TODO: list properties */
+static void emit_interfaces_removed(struct generic_data *data)
+{
+       DBusMessage *signal;
+       DBusMessageIter iter, array;
+       struct generic_data *parent = data->parent;
 
-       dbus_message_iter_close_container(iter, &dict);
+       if (parent == NULL)
+               return;
+
+       signal = dbus_message_new_signal(parent->path,
+                                       DBUS_INTERFACE_OBJECT_MANAGER,
+                                       "InterfacesRemoved");
+       if (signal == NULL)
+               return;
+
+       dbus_message_iter_init_append(signal, &iter);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+                                                               &data->path);
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       DBUS_TYPE_STRING_AS_STRING, &array);
+
+       g_slist_foreach(data->removed, append_name, &array);
+       g_slist_free_full(data->removed, g_free);
+       data->removed = NULL;
+
+       dbus_message_iter_close_container(&iter, &array);
+
+       g_dbus_send_message(data->conn, signal);
 }
 
-static void append_interface(gpointer data, gpointer user_data)
+static gboolean process_changes(gpointer user_data)
 {
-       struct interface_data *iface = data;
-       DBusMessageIter *array = user_data;
-       DBusMessageIter entry;
+       struct generic_data *data = user_data;
 
-       dbus_message_iter_open_container(array, DBUS_TYPE_DICT_ENTRY, NULL,
-                                                               &entry);
-       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &iface->name);
-       append_properties(data, &entry);
-       dbus_message_iter_close_container(array, &entry);
+       data->process_id = 0;
+
+       if (data->added != NULL)
+               emit_interfaces_added(data);
+
+       if (data->removed != NULL)
+               emit_interfaces_removed(data);
+
+       return FALSE;
 }
 
+static void generic_unregister(DBusConnection *connection, void *user_data)
+{
+       struct generic_data *data = user_data;
+       struct generic_data *parent = data->parent;
+
+       if (parent != NULL)
+               parent->objects = g_slist_remove(parent->objects, data);
+
+       if (data->process_id > 0) {
+               g_source_remove(data->process_id);
+               if (data->removed != NULL)
+                       emit_interfaces_removed(data);
+       }
+
+       g_slist_foreach(data->objects, reset_parent, data->parent);
+       g_slist_free(data->objects);
+
+       dbus_connection_unref(data->conn);
+       g_free(data->introspect);
+       g_free(data->path);
+       g_free(data);
+}
+
+static DBusHandlerResult generic_message(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct generic_data *data = user_data;
+       struct interface_data *iface;
+       const GDBusMethodTable *method;
+       const char *interface;
+
+       interface = dbus_message_get_interface(message);
+
+       iface = find_interface(data->interfaces, interface);
+       if (iface == NULL)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       for (method = iface->methods; method &&
+                       method->name && method->function; method++) {
+               if (dbus_message_is_method_call(message, iface->name,
+                                                       method->name) == FALSE)
+                       continue;
+
+               if (g_dbus_args_have_signature(method->in_args,
+                                                       message) == FALSE)
+                       continue;
+
+               if (check_privilege(connection, message, method,
+                                               iface->user_data) == TRUE)
+                       return DBUS_HANDLER_RESULT_HANDLED;
+
+               return process_message(connection, message, method,
+                                                       iface->user_data);
+       }
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusObjectPathVTable generic_table = {
+       .unregister_function    = generic_unregister,
+       .message_function       = generic_message,
+};
+
+static const GDBusMethodTable introspect_methods[] = {
+       { GDBUS_METHOD("Introspect", NULL,
+                       GDBUS_ARGS({ "xml", "s" }), introspect) },
+       { }
+};
+
 static void append_interfaces(struct generic_data *data, DBusMessageIter *iter)
 {
        DBusMessageIter array;
@@ -973,43 +1107,6 @@ static const GDBusSignalTable manager_signals[] = {
        { }
 };
 
-static void emit_interface_added(struct generic_data *data,
-                                               struct interface_data *iface)
-{
-       DBusMessage *signal;
-       DBusMessageIter iter, array;
-       struct generic_data *parent = data->parent;
-
-       if (parent == NULL)
-               return;
-
-       signal = dbus_message_new_signal(parent->path,
-                                       DBUS_INTERFACE_OBJECT_MANAGER,
-                                       "InterfacesAdded");
-       if (signal == NULL)
-               return;
-
-       dbus_message_iter_init_append(signal, &iter);
-       dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
-                                                               &data->path);
-
-       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
-                               DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
-                               DBUS_TYPE_STRING_AS_STRING
-                               DBUS_TYPE_ARRAY_AS_STRING
-                               DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
-                               DBUS_TYPE_STRING_AS_STRING
-                               DBUS_TYPE_VARIANT_AS_STRING
-                               DBUS_DICT_ENTRY_END_CHAR_AS_STRING
-                               DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array);
-
-       append_interface(iface, &array);
-
-       dbus_message_iter_close_container(&iter, &array);
-
-       g_dbus_send_message(data->conn, signal);
-}
-
 static void add_interface(struct generic_data *data,
                                const char *name,
                                const GDBusMethodTable *methods,
@@ -1029,8 +1126,14 @@ static void add_interface(struct generic_data *data,
        iface->destroy = destroy;
 
        data->interfaces = g_slist_append(data->interfaces, iface);
+       if (data->parent == NULL)
+               return;
+
+       data->added = g_slist_append(data->added, iface);
+       if (data->process_id > 0)
+               return;
 
-       emit_interface_added(data, iface);
+       data->process_id = g_idle_add(process_changes, data);
 }
 
 static struct generic_data *object_path_ref(DBusConnection *connection,
@@ -1074,55 +1177,6 @@ static struct generic_data *object_path_ref(DBusConnection *connection,
        return data;
 }
 
-static void emit_interface_remove(struct generic_data *data,
-                                               struct interface_data *iface)
-{
-       DBusMessage *signal;
-       DBusMessageIter iter, array;
-       struct generic_data *parent = data->parent;
-
-       if (parent == NULL)
-               return;
-
-       signal = dbus_message_new_signal(parent->path,
-                                       DBUS_INTERFACE_OBJECT_MANAGER,
-                                       "InterfacesRemoved");
-       if (signal == NULL)
-               return;
-
-       dbus_message_iter_init_append(signal, &iter);
-       dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
-                                                               &data->path);
-
-       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
-                                       DBUS_TYPE_STRING_AS_STRING, &array);
-       dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &iface->name);
-       dbus_message_iter_close_container(&iter, &array);
-
-       g_dbus_send_message(data->conn, signal);
-}
-
-static gboolean remove_interface(struct generic_data *data, const char *name)
-{
-       struct interface_data *iface;
-
-       iface = find_interface(data->interfaces, name);
-       if (iface == NULL)
-               return FALSE;
-
-       emit_interface_remove(data, iface);
-
-       data->interfaces = g_slist_remove(data->interfaces, iface);
-
-       if (iface->destroy)
-               iface->destroy(iface->user_data);
-
-       g_free(iface->name);
-       g_free(iface);
-
-       return TRUE;
-}
-
 static void object_path_unref(DBusConnection *connection, const char *path)
 {
        struct generic_data *data = NULL;
@@ -1143,9 +1197,9 @@ static void object_path_unref(DBusConnection *connection, const char *path)
        remove_interface(data, DBUS_INTERFACE_PROPERTIES);
        remove_interface(data, DBUS_INTERFACE_OBJECT_MANAGER);
 
-       invalidate_parent_data(connection, path);
+       invalidate_parent_data(data->conn, data->path);
 
-       dbus_connection_unregister_object_path(connection, path);
+       dbus_connection_unregister_object_path(data->conn, data->path);
 }
 
 static gboolean check_signal(DBusConnection *conn, const char *path,
@@ -1270,7 +1324,7 @@ gboolean g_dbus_unregister_interface(DBusConnection *connection,
        g_free(data->introspect);
        data->introspect = NULL;
 
-       object_path_unref(connection, path);
+       object_path_unref(connection, data->path);
 
        return TRUE;
 }