Make the QtDBus object tree keep a count of active children

The new member variable activeChildren shall contain the number of
direct children that are active. This number differs from
children.count() because the vector may contain empty entries that
haven't been garbage-collected yet (obj == NULL and activeChildren ==
0).

When this count drops to zero, we know we can simply erase the vector of
children.

Change-Id: Ia20604d3fac852ea4a6e8862d934fbb936fa5e18
Reviewed-by: David Faure (KDE) <faure@kde.org>
This commit is contained in:
Thiago Macieira 2013-01-16 19:41:49 +08:00 committed by The Qt Project
parent 18c7ce5994
commit 3c6bb0ed8b
4 changed files with 45 additions and 13 deletions

View File

@ -803,13 +803,8 @@ bool QDBusConnection::registerObject(const QString &path, QObject *object, Regis
return false;
if (options & QDBusConnectionPrivate::VirtualObject) {
// technically the check for children needs to go even deeper
if (options & SubPath) {
foreach (const QDBusConnectionPrivate::ObjectTreeNode &child, node->children) {
if (child.obj)
return false;
}
}
if (options & SubPath && node->activeChildren)
return false;
} else {
if ((options & ExportChildObjects && !node->children.isEmpty()))
return false;
@ -846,6 +841,7 @@ bool QDBusConnection::registerObject(const QString &path, QObject *object, Regis
}
} else {
// add entry
++node->activeChildren;
node = node->children.insert(it, pathComponents.at(i));
}

View File

@ -140,14 +140,16 @@ public:
{
typedef QVector<ObjectTreeNode> DataList;
inline ObjectTreeNode() : obj(0), flags(0) { }
inline ObjectTreeNode() : obj(0), flags(0), activeChildren(0) { }
inline ObjectTreeNode(const QString &n) // intentionally implicit
: name(n), obj(0), flags(0) { }
: name(n), obj(0), flags(0), activeChildren(0) { }
inline ~ObjectTreeNode() { }
inline bool operator<(const QString &other) const
{ return name < other; }
inline bool operator<(const QStringRef &other) const
{ return QStringRef(&name) < other; }
inline bool isActive() const
{ return obj || activeChildren; }
QString name;
union {
@ -155,6 +157,7 @@ public:
QDBusVirtualObject *treeNode;
};
int flags;
int activeChildren;
DataList children;
};

View File

@ -591,13 +591,23 @@ static void huntAndDestroy(QObject *needle, QDBusConnectionPrivate::ObjectTreeNo
{
QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator it = haystack.children.begin();
QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator end = haystack.children.end();
for ( ; it != end; ++it)
for ( ; it != end; ++it) {
if (!it->isActive())
continue;
huntAndDestroy(needle, *it);
if (!it->isActive())
--haystack.activeChildren;
}
if (needle == haystack.obj) {
haystack.obj = 0;
haystack.flags = 0;
}
if (haystack.activeChildren == 0) {
// quick clean-up, if we're out of children
haystack.children.clear();
}
}
static void huntAndUnregister(const QStringList &pathComponents, int i, QDBusConnection::UnregisterMode mode,
@ -610,6 +620,7 @@ static void huntAndUnregister(const QStringList &pathComponents, int i, QDBusCon
if (mode == QDBusConnection::UnregisterTree) {
// clear the sub-tree as well
node->activeChildren = 0;
node->children.clear(); // can't disconnect the objects because we really don't know if they can
// be found somewhere else in the path too
}
@ -618,10 +629,17 @@ static void huntAndUnregister(const QStringList &pathComponents, int i, QDBusCon
QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator end = node->children.end();
QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator it =
std::lower_bound(node->children.begin(), end, pathComponents.at(i));
if (it == end || it->name != pathComponents.at(i))
if (it == end || it->name != pathComponents.at(i) || !it->isActive())
return; // node not found
huntAndUnregister(pathComponents, i + 1, mode, it);
if (!it->isActive())
--node->activeChildren;
if (node->activeChildren == 0) {
// quick clean-up, if we're out of children
node->children.clear();
}
}
}
@ -631,8 +649,10 @@ static void huntAndEmit(DBusConnection *connection, DBusMessage *msg,
{
QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator it = haystack.children.constBegin();
QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator end = haystack.children.constEnd();
for ( ; it != end; ++it)
huntAndEmit(connection, msg, needle, *it, isScriptable, isAdaptor, path + QLatin1Char('/') + it->name);
for ( ; it != end; ++it) {
if (it->isActive())
huntAndEmit(connection, msg, needle, *it, isScriptable, isAdaptor, path + QLatin1Char('/') + it->name);
}
if (needle == haystack.obj) {
// is this a signal we should relay?

View File

@ -1200,6 +1200,19 @@ void tst_QDBusConnection::registerVirtualObject()
QVERIFY(!con.registerVirtualObject(path, &obj, QDBusConnection::SubPath));
QCOMPARE(con.objectRegisteredAt(path), static_cast<QObject *>(0));
}
{
// Register object, make sure no SubPath handling object can be registered on a parent path.
// (same as above, but deeper)
QObject objectAtSubPath;
QVERIFY(con.registerObject(childChildPath, &objectAtSubPath));
QCOMPARE(con.objectRegisteredAt(childChildPath), static_cast<QObject *>(&objectAtSubPath));
VirtualObject obj;
QVERIFY(!con.registerVirtualObject(path, &obj, QDBusConnection::SubPath));
QCOMPARE(con.objectRegisteredAt(path), static_cast<QObject *>(0));
}
QCOMPARE(con.objectRegisteredAt(path), static_cast<QObject *>(0));
QCOMPARE(con.objectRegisteredAt(childPath), static_cast<QObject *>(0));
QCOMPARE(con.objectRegisteredAt(childChildPath), static_cast<QObject *>(0));