[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