[Telepathy-commits] [telepathy-qt4/master] First go at a not-so-trivial-anymore implementation of the handle machinery.
Olli Salli
olli.salli at collabora.co.uk
Wed Jan 21 04:42:16 PST 2009
Beware: this is compile-shipped late night code and will probably need heavy
ironing out.
---
TelepathyQt4/cli-connection.cpp | 172 +++++++++++++++++++++++++++++++++-
TelepathyQt4/cli-connection.h | 4 +
TelepathyQt4/cli-pending-handles.cpp | 10 ++-
TelepathyQt4/cli-pending-handles.h | 2 +-
4 files changed, 180 insertions(+), 8 deletions(-)
diff --git a/TelepathyQt4/cli-connection.cpp b/TelepathyQt4/cli-connection.cpp
index 7d4305c..b9b41f3 100644
--- a/TelepathyQt4/cli-connection.cpp
+++ b/TelepathyQt4/cli-connection.cpp
@@ -26,6 +26,9 @@
#include <TelepathyQt4/cli-connection.moc.hpp>
#include <QMap>
+#include <QMetaObject>
+#include <QMutex>
+#include <QMutexLocker>
#include <QQueue>
#include <QString>
#include <QtGlobal>
@@ -62,6 +65,36 @@ struct Connection::Private
StatusSpecMap presenceStatuses;
SimpleStatusSpecMap simplePresenceStatuses;
+ // Handle tracking
+ struct HandleContext
+ {
+ int refcount;
+ QMutex lock;
+
+ struct Type
+ {
+ QMap<uint, uint> refcounts;
+ QSet<uint> toRelease;
+ uint requestsInFlight;
+ bool releaseScheduled;
+
+ Type()
+ {
+ requestsInFlight = 0;
+ releaseScheduled = false;
+ }
+ };
+ QMap<uint, Type> types;
+
+ HandleContext()
+ {
+ refcount = 0;
+ }
+ };
+ static QMap<QString, HandleContext*> handleContexts;
+ static QMutex handleContextsLock;
+ HandleContext* handleContext;
+
Private(Connection &parent)
: parent(parent)
{
@@ -88,6 +121,50 @@ struct Connection::Private
parent.connect(watcher,
SIGNAL(finished(QDBusPendingCallWatcher*)),
SLOT(gotStatus(QDBusPendingCallWatcher*)));
+
+ QMutexLocker locker(&handleContextsLock);
+ QString connectionName = parent.connection().name();
+
+ if (handleContexts.contains(connectionName)) {
+ debug() << "Reusing existing HandleContext";
+ handleContext = handleContexts[connectionName];
+ } else {
+ debug() << "Creating new HandleContext";
+ handleContext = new HandleContext;
+ handleContexts[connectionName] = handleContext;
+ }
+
+ // All handle contexts locked, so safe
+ ++handleContext->refcount;
+ }
+
+ ~Private()
+ {
+ QMutexLocker locker(&handleContextsLock);
+
+ // All handle contexts locked, so safe
+ if (!--handleContext->refcount) {
+ debug() << "Destroying HandleContext";
+
+ foreach (uint handleType, handleContext->types.keys()) {
+ HandleContext::Type type = handleContext->types[handleType];
+
+ if (!type.refcounts.empty()) {
+ debug() << " Still had references to" << type.refcounts.size() << "handles, releasing now";
+ parent.ReleaseHandles(handleType, type.refcounts.keys());
+ }
+
+ if (!type.toRelease.empty()) {
+ debug() << " Was going to release" << type.toRelease.size() << "handles, doing that now";
+ parent.ReleaseHandles(handleType, type.toRelease.toList());
+ }
+ }
+
+ handleContexts.remove(parent.connection().name());
+ delete handleContext;
+ } else {
+ Q_ASSERT(handleContext->refcount > 0);
+ }
}
void introspectAliasing()
@@ -208,6 +285,9 @@ struct Connection::Private
}
};
+QMap<QString, Connection::Private::HandleContext*> Connection::Private::handleContexts;
+QMutex Connection::Private::handleContextsLock;
+
Connection::Connection(const QString& serviceName,
const QString& objectPath,
QObject* parent)
@@ -475,6 +555,10 @@ PendingOperation* Connection::requestConnect()
PendingHandles* Connection::requestHandles(uint handleType, const QStringList& names)
{
+ Private::HandleContext *handleContext = mPriv->handleContext;
+ QMutexLocker locker(&handleContext->lock);
+ handleContext->types[handleType].requestsInFlight++;
+
debug() << "Request for" << names.length() << "handles of type" << handleType;
PendingHandles* pending =
@@ -490,12 +574,25 @@ PendingHandles* Connection::requestHandles(uint handleType, const QStringList& n
PendingHandles* Connection::referenceHandles(uint handleType, const UIntList& handles)
{
+ Private::HandleContext *handleContext = mPriv->handleContext;
+ QMutexLocker locker(&handleContext->lock);
+
debug() << "Reference of" << handles.length() << "handles of type" << handleType;
+ UIntList alreadyHeld;
+ UIntList notYetHeld;
+ foreach (uint handle, handles) {
+ if (handleContext->types[handleType].refcounts.contains(handle) || handleContext->types[handleType].toRelease.contains(handle))
+ alreadyHeld.push_back(handle);
+ else
+ notYetHeld.push_back(handle);
+ }
+ debug() << " Already holding" << alreadyHeld.size() << "of the handles -" << notYetHeld.size() << "to go";
+
PendingHandles* pending =
- new PendingHandles(this, handleType, handles, false);
+ new PendingHandles(this, handleType, handles, alreadyHeld);
QDBusPendingCallWatcher* watcher =
- new QDBusPendingCallWatcher(HoldHandles(handleType, handles), pending);
+ new QDBusPendingCallWatcher(HoldHandles(handleType, notYetHeld), pending);
pending->connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
SLOT(onCallFinished(QDBusPendingCallWatcher*)));
@@ -510,12 +607,79 @@ PendingOperation* Connection::requestDisconnect()
void Connection::refHandle(uint type, uint handle)
{
- // FIXME implement
+ Private::HandleContext *handleContext = mPriv->handleContext;
+ QMutexLocker locker(&handleContext->lock);
+
+ if (handleContext->types[type].toRelease.contains(handle))
+ handleContext->types[type].toRelease.remove(handle);
+
+ handleContext->types[type].refcounts[handle]++;
}
void Connection::unrefHandle(uint type, uint handle)
{
- // FIXME implement
+ Private::HandleContext *handleContext = mPriv->handleContext;
+ QMutexLocker locker(&handleContext->lock);
+
+ Q_ASSERT(handleContext->types.contains(type));
+ Q_ASSERT(handleContext->types[type].refcounts.contains(handle));
+
+ if (!--handleContext->types[type].refcounts[handle]) {
+ handleContext->types[type].refcounts.remove(handle);
+ handleContext->types[type].toRelease.insert(handle);
+
+ if (!handleContext->types[type].releaseScheduled) {
+ if (!handleContext->types[type].requestsInFlight) {
+ debug() << "Lost last reference to at least one handle of type" << type << "and no requests in flight for that type - scheduling a release sweep";
+ QMetaObject::invokeMethod(this, SLOT("doReleaseSweep(uint)"), Qt::QueuedConnection, Q_ARG(uint, type));
+ handleContext->types[type].releaseScheduled = true;
+ } else {
+ debug() << "Deferring handle release sweep for type" << type << "to when there are no requests in flight for that type";
+ }
+ }
+ }
+}
+
+void Connection::doReleaseSweep(uint type)
+{
+ Private::HandleContext *handleContext = mPriv->handleContext;
+ QMutexLocker locker(&handleContext->lock);
+
+ Q_ASSERT(handleContext->types[type].releaseScheduled);
+ Q_ASSERT(handleContext->types.contains(type));
+
+ debug() << "Entering handle release sweep for type" << type;
+ handleContext->types[type].releaseScheduled = false;
+
+ if (handleContext->types[type].requestsInFlight > 0) {
+ debug() << " There are requests in flight, deferring sweep to when they have been completed";
+ return;
+ }
+
+ if (handleContext->types[type].toRelease.isEmpty()) {
+ debug() << " No handles to release - every one has been resurrected";
+ return;
+ }
+
+ debug() << " Releasing" << handleContext->types[type].toRelease.size() << "handles";
+
+ ReleaseHandles(type, handleContext->types[type].toRelease.toList());
+ handleContext->types[type].toRelease.clear();
+}
+
+void Connection::handleRequestLanded(uint type)
+{
+ Private::HandleContext *handleContext = mPriv->handleContext;
+ QMutexLocker locker(&handleContext->lock);
+
+ Q_ASSERT(handleContext->types.contains(type));
+ Q_ASSERT(handleContext->types[type].requestsInFlight > 0);
+
+ if (!--handleContext->types[type].requestsInFlight && !handleContext->types[type].toRelease.isEmpty() && !handleContext->types[type].releaseScheduled) {
+ debug() << "All handle requests for type" << type << "landed and there are handles of that type to release - scheduling a release sweep";
+ QMetaObject::invokeMethod(this, SLOT("doReleaseSweep(uint)"), Qt::QueuedConnection, Q_ARG(uint, type));
+ handleContext->types[type].releaseScheduled = true;
+ }
}
}
diff --git a/TelepathyQt4/cli-connection.h b/TelepathyQt4/cli-connection.h
index 1140831..c88f06f 100644
--- a/TelepathyQt4/cli-connection.h
+++ b/TelepathyQt4/cli-connection.h
@@ -502,10 +502,14 @@ private Q_SLOTS:
void gotStatuses(QDBusPendingCallWatcher* watcher);
void gotSimpleStatuses(QDBusPendingCallWatcher* watcher);
+ void doReleaseSweep(uint type);
+
private:
+ friend class PendingHandles;
friend class ReferencedHandles;
void refHandle(uint type, uint handle);
void unrefHandle(uint type, uint handle);
+ void handleRequestLanded(uint type);
struct Private;
friend struct Private;
diff --git a/TelepathyQt4/cli-pending-handles.cpp b/TelepathyQt4/cli-pending-handles.cpp
index d0326ff..fcd76a2 100644
--- a/TelepathyQt4/cli-pending-handles.cpp
+++ b/TelepathyQt4/cli-pending-handles.cpp
@@ -38,6 +38,7 @@ struct PendingHandles::Private
QStringList namesRequested;
UIntList handlesToReference;
ReferencedHandles handles;
+ ReferencedHandles alreadyHeld;
};
PendingHandles::PendingHandles(Connection* connection, uint handleType, const QStringList& names)
@@ -49,16 +50,17 @@ PendingHandles::PendingHandles(Connection* connection, uint handleType, const QS
mPriv->namesRequested = names;
}
-PendingHandles::PendingHandles(Connection* connection, uint handleType, const UIntList& handles, bool allHeld)
+PendingHandles::PendingHandles(Connection* connection, uint handleType, const UIntList& handles, const UIntList& alreadyHeld)
: PendingOperation(connection), mPriv(new Private)
{
mPriv->connection = connection;
mPriv->handleType = handleType;
mPriv->isRequest = false;
mPriv->handlesToReference = handles;
+ mPriv->alreadyHeld = ReferencedHandles(connection, handleType, alreadyHeld);
- if (allHeld) {
- mPriv->handles = ReferencedHandles(connection, handleType, handles);
+ if (alreadyHeld == handles) {
+ mPriv->handles = mPriv->alreadyHeld;
setFinished();
}
}
@@ -118,6 +120,8 @@ void PendingHandles::onCallFinished(QDBusPendingCallWatcher* watcher)
mPriv->handles = ReferencedHandles(connection(), handleType(), reply.value());
setFinished();
}
+
+ connection()->handleRequestLanded(handleType());
} else {
QDBusPendingReply<void> reply;
diff --git a/TelepathyQt4/cli-pending-handles.h b/TelepathyQt4/cli-pending-handles.h
index 04c82b8..c51a4e0 100644
--- a/TelepathyQt4/cli-pending-handles.h
+++ b/TelepathyQt4/cli-pending-handles.h
@@ -153,7 +153,7 @@ private:
friend class Connection;
PendingHandles(Connection* connection, uint handleType, const QStringList& names);
- PendingHandles(Connection* connection, uint handleType, const UIntList& handles, bool allHeld);
+ PendingHandles(Connection* connection, uint handleType, const UIntList& handles, const UIntList& alreadyHeld);
struct Private;
friend struct Private;
--
1.5.6.5
More information about the Telepathy-commits
mailing list