[PATCH 1/5] dbus: Coalesce InterfacesAdded/Removed signals
by Andrew Zaborowski
When multiple interfaces are added or removed during the same main loop
cycle emit one signal per object with all the interfaces added or
removed. Move the signals to the idle callback.
---
ell/dbus-service.c | 337 ++++++++++++++++++++++++++++++++++++++++-------------
1 file changed, 257 insertions(+), 80 deletions(-)
diff --git a/ell/dbus-service.c b/ell/dbus-service.c
index afd0ed9..3e8005d 100644
--- a/ell/dbus-service.c
+++ b/ell/dbus-service.c
@@ -38,6 +38,7 @@
#include "dbus-service.h"
#include "dbus-private.h"
#include "private.h"
+#include "idle.h"
#define XML_ID "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
#define XML_DTD "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"
@@ -104,6 +105,20 @@ struct object_node {
struct object_manager {
char *path;
struct l_dbus *dbus;
+ struct l_queue *announce_added;
+ struct l_queue *announce_removed;
+};
+
+struct interface_add_record {
+ char *path;
+ struct object_node *object;
+ struct l_queue *instances;
+};
+
+struct interface_remove_record {
+ char *path;
+ struct object_node *object;
+ struct l_queue *interface_names;
};
struct _dbus_object_tree {
@@ -111,6 +126,7 @@ struct _dbus_object_tree {
struct l_hashmap *objects;
struct object_node *root;
struct l_queue *object_managers;
+ struct l_idle *emit_signals_work;
};
void _dbus_method_introspection(struct _dbus_method *info,
@@ -520,6 +536,24 @@ static bool match_interface_instance(const void *a, const void *b)
return false;
}
+static void interfaces_added_rec_free(void *data)
+{
+ struct interface_add_record *rec = data;
+
+ l_free(rec->path);
+ l_queue_destroy(rec->instances, NULL);
+ l_free(rec);
+}
+
+static void interfaces_removed_rec_free(void *data)
+{
+ struct interface_remove_record *rec = data;
+
+ l_free(rec->path);
+ l_queue_destroy(rec->interface_names, l_free);
+ l_free(rec);
+}
+
static void properties_setup_func(struct l_dbus_interface *);
static void object_manager_setup_func(struct l_dbus_interface *);
@@ -578,6 +612,8 @@ static void object_manager_free(void *data)
struct object_manager *manager = data;
l_free(manager->path);
+ l_queue_destroy(manager->announce_added, interfaces_added_rec_free);
+ l_queue_destroy(manager->announce_removed, interfaces_removed_rec_free);
l_free(manager);
}
@@ -591,6 +627,9 @@ void _dbus_object_tree_free(struct _dbus_object_tree *tree)
l_queue_destroy(tree->object_managers, object_manager_free);
+ if (tree->emit_signals_work)
+ l_idle_remove(tree->emit_signals_work);
+
l_free(tree);
}
@@ -764,6 +803,163 @@ bool _dbus_object_tree_object_destroy(struct _dbus_object_tree *tree,
return true;
}
+static bool get_properties_dict(struct l_dbus *dbus,
+ struct l_dbus_message *message,
+ struct l_dbus_message_builder *builder,
+ const struct l_dbus_interface *interface,
+ void *user_data)
+{
+ const struct l_queue_entry *entry;
+ const struct _dbus_property *property;
+ const char *signature;
+
+ l_dbus_message_builder_enter_array(builder, "{sv}");
+
+ for (entry = l_queue_get_entries(interface->properties); entry;
+ entry = entry->next) {
+ property = entry->data;
+ signature = property->metainfo + strlen(property->metainfo) + 1;
+
+ l_dbus_message_builder_enter_dict(builder, "sv");
+ l_dbus_message_builder_append_basic(builder, 's',
+ property->metainfo);
+ l_dbus_message_builder_enter_variant(builder, signature);
+
+ if (!property->getter(dbus, message, builder, user_data))
+ return false;
+
+ l_dbus_message_builder_leave_variant(builder);
+ l_dbus_message_builder_leave_dict(builder);
+ }
+
+ l_dbus_message_builder_leave_array(builder);
+
+ return true;
+}
+
+static struct l_dbus_message *build_interfaces_removed_signal(
+ const struct object_manager *manager,
+ const struct interface_remove_record *rec)
+{
+ struct l_dbus_message *signal;
+ struct l_dbus_message_builder *builder;
+ const struct l_queue_entry *entry;
+
+ signal = l_dbus_message_new_signal(manager->dbus, manager->path,
+ DBUS_INTERFACE_OBJECT_MANAGER,
+ "InterfacesRemoved");
+
+ builder = l_dbus_message_builder_new(signal);
+
+ l_dbus_message_builder_append_basic(builder, 'o', rec->path);
+ l_dbus_message_builder_enter_array(builder, "s");
+
+ for (entry = l_queue_get_entries(rec->interface_names); entry;
+ entry = entry->next)
+ l_dbus_message_builder_append_basic(builder, 's', entry->data);
+
+ l_dbus_message_builder_leave_array(builder);
+ l_dbus_message_builder_finalize(builder);
+ l_dbus_message_builder_destroy(builder);
+
+ return signal;
+}
+
+static struct l_dbus_message *build_interfaces_added_signal(
+ const struct object_manager *manager,
+ const struct interface_add_record *rec)
+{
+ struct l_dbus_message *signal;
+ struct l_dbus_message_builder *builder;
+ const struct l_queue_entry *entry;
+ const struct interface_instance *instance;
+
+ signal = l_dbus_message_new_signal(manager->dbus, manager->path,
+ DBUS_INTERFACE_OBJECT_MANAGER,
+ "InterfacesAdded");
+
+ builder = l_dbus_message_builder_new(signal);
+
+ l_dbus_message_builder_append_basic(builder, 'o', rec->path);
+ l_dbus_message_builder_enter_array(builder, "{sa{sv}}");
+
+ for (entry = l_queue_get_entries(rec->instances); entry;
+ entry = entry->next) {
+ instance = entry->data;
+
+ l_dbus_message_builder_enter_dict(builder, "sa{sv}");
+ l_dbus_message_builder_append_basic(builder, 's',
+ instance->interface->name);
+
+ if (!get_properties_dict(manager->dbus, signal, builder,
+ instance->interface,
+ instance->user_data)) {
+ l_dbus_message_builder_destroy(builder);
+ l_dbus_message_unref(signal);
+
+ return NULL;
+ }
+
+ l_dbus_message_builder_leave_dict(builder);
+ }
+
+ l_dbus_message_builder_leave_array(builder);
+ l_dbus_message_builder_finalize(builder);
+ l_dbus_message_builder_destroy(builder);
+
+ return signal;
+}
+
+static void emit_signals(struct l_idle *idle, void *user_data)
+{
+ struct l_dbus *dbus = user_data;
+ struct _dbus_object_tree *tree = _dbus_get_tree(dbus);
+ struct interface_add_record *interfaces_added_rec;
+ struct interface_remove_record *interfaces_removed_rec;
+ struct property_changes *property_changes_rec;
+ const struct l_queue_entry *entry;
+ struct object_manager *manager;
+ struct l_dbus_message *signal;
+
+ l_idle_remove(tree->emit_signals_work);
+ tree->emit_signals_work = NULL;
+
+ for (entry = l_queue_get_entries(tree->object_managers); entry;
+ entry = entry->next) {
+ manager = entry->data;
+
+ while ((interfaces_removed_rec =
+ l_queue_pop_head(manager->announce_removed))) {
+ signal = build_interfaces_removed_signal(manager,
+ interfaces_removed_rec);
+ interfaces_removed_rec_free(interfaces_removed_rec);
+
+ if (signal)
+ l_dbus_send(manager->dbus, signal);
+ }
+
+ while ((interfaces_added_rec =
+ l_queue_pop_head(manager->announce_added))) {
+ signal = build_interfaces_added_signal(manager,
+ interfaces_added_rec);
+ interfaces_added_rec_free(interfaces_added_rec);
+
+ if (signal)
+ l_dbus_send(manager->dbus, signal);
+ }
+ }
+}
+
+static void schedule_emit_signals(struct l_dbus *dbus)
+{
+ struct _dbus_object_tree *tree = _dbus_get_tree(dbus);
+
+ if (tree->emit_signals_work)
+ return;
+
+ tree->emit_signals_work = l_idle_create(emit_signals, dbus, NULL);
+}
+
/* Send the signals associated with a property value change */
bool _dbus_object_tree_property_changed(struct l_dbus *dbus,
const char *path,
@@ -941,40 +1137,6 @@ static struct l_dbus_message *old_set_property(struct l_dbus *dbus,
return NULL;
}
-static bool get_properties_dict(struct l_dbus *dbus,
- struct l_dbus_message *message,
- struct l_dbus_message_builder *builder,
- const struct l_dbus_interface *interface,
- void *user_data)
-{
- const struct l_queue_entry *entry;
- const struct _dbus_property *property;
- const char *signature;
-
- l_dbus_message_builder_enter_array(builder, "{sv}");
-
- for (entry = l_queue_get_entries(interface->properties); entry;
- entry = entry->next) {
- property = entry->data;
- signature = property->metainfo + strlen(property->metainfo) + 1;
-
- l_dbus_message_builder_enter_dict(builder, "sv");
- l_dbus_message_builder_append_basic(builder, 's',
- property->metainfo);
- l_dbus_message_builder_enter_variant(builder, signature);
-
- if (!property->getter(dbus, message, builder, user_data))
- return false;
-
- l_dbus_message_builder_leave_variant(builder);
- l_dbus_message_builder_leave_dict(builder);
- }
-
- l_dbus_message_builder_leave_array(builder);
-
- return true;
-}
-
static struct l_dbus_message *old_get_properties(struct l_dbus *dbus,
struct l_dbus_message *message,
void *user_data)
@@ -1088,42 +1250,18 @@ bool _dbus_object_tree_unregister_interface(struct _dbus_object_tree *tree,
return true;
}
-static bool build_interfaces_added_signal(const struct object_manager *manager,
- const char *path,
- const struct interface_instance *instance)
+static bool match_interfaces_added_object(const void *a, const void *b)
{
- struct l_dbus_message *signal;
- struct l_dbus_message_builder *builder;
-
- signal = l_dbus_message_new_signal(manager->dbus, manager->path,
- DBUS_INTERFACE_OBJECT_MANAGER,
- "InterfacesAdded");
- builder = l_dbus_message_builder_new(signal);
-
- l_dbus_message_builder_append_basic(builder, 'o', path);
- l_dbus_message_builder_enter_array(builder, "{sa{sv}}");
- l_dbus_message_builder_enter_dict(builder, "sa{sv}");
- l_dbus_message_builder_append_basic(builder, 's',
- instance->interface->name);
-
- if (!get_properties_dict(manager->dbus, signal, builder,
- instance->interface,
- instance->user_data)) {
- l_dbus_message_builder_destroy(builder);
- l_dbus_message_unref(signal);
-
- return false;
- }
-
- l_dbus_message_builder_leave_dict(builder);
- l_dbus_message_builder_leave_array(builder);
+ const struct interface_add_record *rec = a;
- l_dbus_message_builder_finalize(builder);
- l_dbus_message_builder_destroy(builder);
+ return rec->object == b;
+}
- l_dbus_send(manager->dbus, signal);
+static bool match_interfaces_removed_object(const void *a, const void *b)
+{
+ const struct interface_add_record *rec = a;
- return true;
+ return rec->object == b;
}
bool _dbus_object_tree_add_interface(struct _dbus_object_tree *tree,
@@ -1136,6 +1274,7 @@ bool _dbus_object_tree_add_interface(struct _dbus_object_tree *tree,
const struct l_queue_entry *entry;
struct object_manager *manager;
size_t path_len;
+ struct interface_add_record *change_rec;
dbi = l_hashmap_lookup(tree->interfaces, interface);
if (!dbi)
@@ -1173,13 +1312,30 @@ bool _dbus_object_tree_add_interface(struct _dbus_object_tree *tree,
path[path_len] != '/' && path_len > 1))
continue;
- build_interfaces_added_signal(manager, path, instance);
+ change_rec = l_queue_find(manager->announce_added,
+ match_interfaces_added_object,
+ object);
+ if (!change_rec) {
+ change_rec = l_new(struct interface_add_record, 1);
+ change_rec->path = l_strdup(path);
+ change_rec->object = object;
+ change_rec->instances = l_queue_new();
+
+ l_queue_push_tail(manager->announce_added, change_rec);
+ }
+
+ /* No need to check for duplicates here */
+ l_queue_push_tail(change_rec->instances, instance);
+
+ schedule_emit_signals(manager->dbus);
}
if (!strcmp(interface, DBUS_INTERFACE_OBJECT_MANAGER)) {
manager = l_new(struct object_manager, 1);
manager->path = l_strdup(path);
manager->dbus = instance->user_data;
+ manager->announce_added = l_queue_new();
+ manager->announce_removed = l_queue_new();
l_queue_push_tail(tree->object_managers, manager);
}
@@ -1201,9 +1357,9 @@ bool _dbus_object_tree_remove_interface(struct _dbus_object_tree *tree,
struct interface_instance *instance;
const struct l_queue_entry *entry;
struct object_manager *manager;
- struct l_dbus_message *signal;
- struct l_dbus_message_builder *builder;
size_t path_len;
+ struct interface_add_record *interfaces_added_rec;
+ struct interface_remove_record *interfaces_removed_rec;
node = l_hashmap_lookup(tree->objects, path);
if (!node)
@@ -1234,19 +1390,40 @@ bool _dbus_object_tree_remove_interface(struct _dbus_object_tree *tree,
path[path_len] != '/' && path_len > 1))
continue;
- signal = l_dbus_message_new_signal(manager->dbus, manager->path,
- DBUS_INTERFACE_OBJECT_MANAGER,
- "InterfacesRemoved");
+ interfaces_added_rec = l_queue_find(manager->announce_added,
+ match_interfaces_added_object,
+ node);
+ if (interfaces_added_rec && l_queue_remove(
+ interfaces_added_rec->instances,
+ instance)) {
+ if (l_queue_isempty(interfaces_added_rec->instances))
+ l_queue_remove(manager->announce_added,
+ interfaces_added_rec);
- builder = l_dbus_message_builder_new(signal);
+ interfaces_added_rec_free(interfaces_added_rec);
- l_dbus_message_builder_append_basic(builder, 'o', path);
- l_dbus_message_builder_enter_array(builder, "s");
- l_dbus_message_builder_append_basic(builder, 's', interface);
- l_dbus_message_builder_leave_array(builder);
- l_dbus_message_builder_finalize(builder);
- l_dbus_message_builder_destroy(builder);
- l_dbus_send(manager->dbus, signal);
+ continue;
+ }
+
+ interfaces_removed_rec = l_queue_find(manager->announce_removed,
+ match_interfaces_removed_object,
+ node);
+ if (!interfaces_removed_rec) {
+ interfaces_removed_rec =
+ l_new(struct interface_remove_record, 1);
+ interfaces_removed_rec->path = l_strdup(path);
+ interfaces_removed_rec->object = node;
+ interfaces_removed_rec->interface_names =
+ l_queue_new();
+ l_queue_push_tail(manager->announce_removed,
+ interfaces_removed_rec);
+ }
+
+ /* No need to check for duplicates here */
+ l_queue_push_tail(interfaces_removed_rec->interface_names,
+ l_strdup(interface));
+
+ schedule_emit_signals(manager->dbus);
}
return true;
--
2.5.0
6 years, 4 months
[PATCH 01/10] dbus: Send InterfacesRemoved on object unregister
by Andrew Zaborowski
Make sure we call _dbus_object_tree_remove_interface to do all the
necessary steps of interface removal when an object from the tree
together with its interfaces.
---
ell/dbus-service.c | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/ell/dbus-service.c b/ell/dbus-service.c
index 1e3bb66..53e2512 100644
--- a/ell/dbus-service.c
+++ b/ell/dbus-service.c
@@ -733,13 +733,24 @@ bool _dbus_object_tree_object_destroy(struct _dbus_object_tree *tree,
const char *path)
{
struct object_node *node;
+ const struct l_queue_entry *entry;
+ const struct interface_instance *instance;
- node = l_hashmap_remove(tree->objects, path);
+ node = l_hashmap_lookup(tree->objects, path);
if (!node)
return false;
- l_queue_destroy(node->instances,
- (l_queue_destroy_func_t) interface_instance_free);
+ while ((entry = l_queue_get_entries(node->instances))) {
+ instance = entry->data;
+
+ if (!_dbus_object_tree_remove_interface(tree, path,
+ instance->interface->name))
+ return false;
+ }
+
+ l_hashmap_remove(tree->objects, path);
+
+ l_queue_destroy(node->instances, NULL);
node->instances = NULL;
if (node->destroy) {
--
2.5.0
6 years, 4 months
[PATCH 10/10] unit: org.freedesktop.DBus.ObjectManager signals tests
by Andrew Zaborowski
---
unit/test-dbus-properties.c | 88 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 88 insertions(+)
diff --git a/unit/test-dbus-properties.c b/unit/test-dbus-properties.c
index acbab29..73172c5 100644
--- a/unit/test-dbus-properties.c
+++ b/unit/test-dbus-properties.c
@@ -789,6 +789,89 @@ static void test_object_manager_get(struct l_dbus *dbus, void *test_data)
NULL, NULL));
}
+static struct l_timeout *om_signal_timeout;
+
+static void om_signal_timeout_callback(struct l_timeout *timeout,
+ void *user_data)
+{
+ om_signal_timeout = NULL;
+ test_assert(false);
+}
+
+static bool expect_interfaces_added;
+
+static void root_signal_callback(struct l_dbus_message *message)
+{
+ const char *path, *interface, *member;
+ struct l_dbus_message_iter interfaces, properties;
+
+ if (!om_signal_timeout)
+ return;
+
+ interface = l_dbus_message_get_interface(message);
+ member = l_dbus_message_get_member(message);
+
+ if (strcmp(interface, "org.freedesktop.DBus.ObjectManager"))
+ return;
+
+ if (!strcmp(member, "InterfacesAdded"))
+ test_assert(expect_interfaces_added);
+ else if (!strcmp(member, "InterfacesRemoved"))
+ test_assert(!expect_interfaces_added);
+ else
+ return;
+
+ if (!strcmp(member, "InterfacesAdded")) {
+ test_assert(l_dbus_message_get_arguments(message, "oa{sa{sv}}",
+ &path,
+ &interfaces));
+ test_assert(!strcmp(path, "/test2"));
+
+ test_assert(l_dbus_message_iter_next_entry(&interfaces,
+ &interface,
+ &properties));
+ test_assert(!strcmp(interface, "org.test"));
+ validate_properties(&properties);
+
+ test_assert(!l_dbus_message_iter_next_entry(&interfaces,
+ &interface,
+ &properties));
+
+ /* Now repeat the test for the InterfacesRemoved signal */
+
+ expect_interfaces_added = false;
+ test_assert(l_dbus_unregister_object(dbus, "/test2"));
+ } else {
+ test_assert(l_dbus_message_get_arguments(message, "oas",
+ &path,
+ &interfaces));
+ test_assert(!strcmp(path, "/test2"));
+
+ test_assert(l_dbus_message_iter_next_entry(&interfaces,
+ &interface));
+ test_assert(!strcmp(interface, "org.test"));
+
+ test_assert(!l_dbus_message_iter_next_entry(&interfaces,
+ &interface));
+
+ l_timeout_remove(om_signal_timeout);
+ om_signal_timeout = NULL;
+
+ test_next();
+ }
+}
+
+static void test_object_manager_signals(struct l_dbus *dbus, void *test_data)
+{
+ om_signal_timeout = l_timeout_create(1, om_signal_timeout_callback,
+ NULL, NULL);
+ test_assert(om_signal_timeout);
+
+ expect_interfaces_added = true;
+ test_assert(l_dbus_object_add_interface(dbus, "/test2", "org.test",
+ NULL));
+}
+
static void signal_message(struct l_dbus_message *message, void *user_data)
{
const char *path;
@@ -797,6 +880,9 @@ static void signal_message(struct l_dbus_message *message, void *user_data)
if (!strcmp(path, "/test"))
test_signal_callback(message);
+
+ if (!strcmp(path, "/"))
+ root_signal_callback(message);
}
int main(int argc, char *argv[])
@@ -872,6 +958,8 @@ int main(int argc, char *argv[])
test_add("Property changed signals", test_property_signals, NULL);
test_add("org.freedesktop.DBus.ObjectManager get",
test_object_manager_get, NULL);
+ test_add("org.freedesktop.DBus.ObjectManager signals",
+ test_object_manager_signals, NULL);
l_main_run();
--
2.5.0
6 years, 4 months
[PATCH 1/2] doc: Recommend "--no-signoff" when invoking checkpatch.pl
by Mat Martineau
---
doc/coding-style.txt | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/doc/coding-style.txt b/doc/coding-style.txt
index c362eef..6b1c44a 100644
--- a/doc/coding-style.txt
+++ b/doc/coding-style.txt
@@ -11,13 +11,13 @@ can get a passport for your patch ASAP.
First of all, the Embedded Linux Library coding style must follow every rule
for the Linux kernel (http://www.kernel.org/doc/Documentation/CodingStyle).
There also exists a tool named checkpatch.pl to help you check the compliance
-with it. Just type "checkpatch.pl --no-tree patch_name" to check your patch.
+with it. Just type "checkpatch.pl --no-tree --no-signoff patch_name" to check
+your patch.
-In theory, you need to clean up all the warnings and errors except this
-one: "ERROR: Missing Signed-off-by: line(s)". ell does not used Signed-Off
-lines, so including them is actually an error. In certain circumstances one
-can ignore the 80 character per line limit. This is generally only allowed if
-the alternative would make the code even less readable.
+In theory, you need to clean up all the warnings and errors. In
+certain circumstances one can ignore the 80 character per line limit.
+This is generally only allowed if the alternative would make the code
+even less readable.
Besides the kernel coding style above, the ell has special
flavors for its own. Some of them are mandatory (marked as 'M'), while
--
2.7.0
6 years, 4 months
[PATCH] dbus: Fix read after free during cleanup
by Andrew Zaborowski
subtree_free will be checking if interfaces of objects being freed have
a destroy callback so the interfaces should not be freed before that.
---
ell/dbus-service.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/ell/dbus-service.c b/ell/dbus-service.c
index 5d49836..1e3bb66 100644
--- a/ell/dbus-service.c
+++ b/ell/dbus-service.c
@@ -583,12 +583,12 @@ static void object_manager_free(void *data)
void _dbus_object_tree_free(struct _dbus_object_tree *tree)
{
+ subtree_free(tree->root);
+
l_hashmap_destroy(tree->interfaces,
(l_hashmap_destroy_func_t) _dbus_interface_free);
l_hashmap_destroy(tree->objects, NULL);
- subtree_free(tree->root);
-
l_queue_destroy(tree->object_managers, object_manager_free);
l_free(tree);
--
2.5.0
6 years, 4 months
[PATCH 1/8] dbus: More complete checks when removing nodes.
by Andrew Zaborowski
This addresses two problems with nodes being freed from the object tree
but not from tree->objects.
---
ell/dbus-service.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/ell/dbus-service.c b/ell/dbus-service.c
index e082226..639872b 100644
--- a/ell/dbus-service.c
+++ b/ell/dbus-service.c
@@ -660,6 +660,9 @@ void _dbus_object_tree_prune_node(struct object_node *node)
if (parent->children != NULL)
return;
+ if (parent->instances)
+ return;
+
node = parent;
parent = node->parent;
}
@@ -741,9 +744,11 @@ bool _dbus_object_tree_unregister(struct _dbus_object_tree *tree,
if (instance)
interface_instance_free(instance);
- if (l_queue_isempty(node->instances) && !node->children) {
+ if (l_queue_isempty(node->instances)) {
l_hashmap_remove(tree->objects, path);
- _dbus_object_tree_prune_node(node);
+
+ if (!node->children)
+ _dbus_object_tree_prune_node(node);
}
return r;
--
2.5.0
6 years, 4 months
[PATCH 1/8] dbus: More complete checks when removing nodes.
by Andrew Zaborowski
This addresses two problems with nodes being freed from the object tree
but not from tree->objects.
With the current semantics for _dbus_object_tree_prune_node to not
incorrectly remove parent nodes that are still in tree->objects it
would be sufficient to check if the node has any interface instances
left. But with the ongoing ObjectManager it's better to directly check
the path against tree->objects.
---
ell/dbus-private.h | 4 ++-
ell/dbus-service.c | 72 ++++++++++++++++++++++++++++++++++++++++--------------
2 files changed, 56 insertions(+), 20 deletions(-)
diff --git a/ell/dbus-private.h b/ell/dbus-private.h
index e60d09e..a0ddb35 100644
--- a/ell/dbus-private.h
+++ b/ell/dbus-private.h
@@ -167,7 +167,9 @@ struct object_node *_dbus_object_tree_makepath(struct _dbus_object_tree *tree,
const char *path);
struct object_node *_dbus_object_tree_lookup(struct _dbus_object_tree *tree,
const char *path);
-void _dbus_object_tree_prune_node(struct object_node *node);
+bool _dbus_object_tree_prune_node(struct _dbus_object_tree *tree,
+ struct object_node *node,
+ const char *path);
bool _dbus_object_tree_register(struct _dbus_object_tree *tree,
const char *path, const char *interface,
diff --git a/ell/dbus-service.c b/ell/dbus-service.c
index e082226..9380740 100644
--- a/ell/dbus-service.c
+++ b/ell/dbus-service.c
@@ -636,33 +636,65 @@ struct object_node *_dbus_object_tree_lookup(struct _dbus_object_tree *tree,
return lookup_recurse(tree->root, path);
}
-void _dbus_object_tree_prune_node(struct object_node *node)
+bool _dbus_object_tree_prune_node(struct _dbus_object_tree *tree,
+ struct object_node *node,
+ const char *path)
{
- struct object_node *parent = node->parent;
- struct child_node *p = NULL, *c;
+ struct object_node *parent;
+ struct child_node *p, *c;
+ struct interface_instance *instance;
+ char parentpath[strlen(path) + 1];
+
+ if (!node) {
+ node = l_hashmap_remove(tree->objects, path);
+ if (!node)
+ return false;
+ }
+
+ while ((instance = l_queue_pop_head(node->instances)))
+ interface_instance_free(instance);
+
+ if (node->children || !node->parent)
+ return true;
- while (parent) {
- for (c = parent->children, p = NULL; c; p = c, c = c->next) {
- if (c->node != node)
- continue;
+ /*
+ * Walk up the tree until a node that either has more than one
+ * child, is the root or is in the objects hashmap.
+ */
+ strcpy(parentpath, path);
- if (p)
- p->next = c->next;
- else
- parent->children = c->next;
+ while (true) {
+ parent = node->parent;
- subtree_free(c->node);
- l_free(c);
+ if (parent == tree->root)
+ break;
+ if (parent->children->next)
break;
- }
- if (parent->children != NULL)
- return;
+ /* Parent's path */
+ parentpath[strlen(parentpath) -
+ strlen(parent->children->subpath) - 1] = '\0';
+
+ if (l_hashmap_lookup(tree->objects, parentpath))
+ break;
node = parent;
- parent = node->parent;
}
+
+ for (c = parent->children, p = NULL; c; p = c, c = c->next)
+ if (c->node == node)
+ break;
+
+ if (p)
+ p->next = c->next;
+ else
+ parent->children = c->next;
+
+ l_free(c);
+ subtree_free(node);
+
+ return true;
}
bool _dbus_object_tree_register(struct _dbus_object_tree *tree,
@@ -741,9 +773,11 @@ bool _dbus_object_tree_unregister(struct _dbus_object_tree *tree,
if (instance)
interface_instance_free(instance);
- if (l_queue_isempty(node->instances) && !node->children) {
+ if (l_queue_isempty(node->instances)) {
l_hashmap_remove(tree->objects, path);
- _dbus_object_tree_prune_node(node);
+
+ if (!node->children)
+ _dbus_object_tree_prune_node(tree, node, path);
}
return r;
--
2.5.0
6 years, 5 months