[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [sink/develop] common: Don't call into the model after the model has been destroyed.
From:       Christian Mollekopf <null () kde ! org>
Date:       2017-01-31 19:33:31
Message-ID: E1cYeBH-0005xb-Kf () code ! kde ! org
[Download RAW message or body]

Git commit a910706986aa8e83e070d23525c02649d80c05c3 by Christian Mollekopf.
Committed on 31/01/2017 at 18:38.
Pushed by cmollekopf into branch 'develop'.

Don't call into the model after the model has been destroyed.

M  +33   -16   common/modelresult.cpp
M  +1    -0    common/modelresult.h
M  +51   -9    common/resultprovider.h

https://commits.kde.org/sink/a910706986aa8e83e070d23525c02649d80c05c3

diff --git a/common/modelresult.cpp b/common/modelresult.cpp
index 695072c..0825518 100644
--- a/common/modelresult.cpp
+++ b/common/modelresult.cpp
@@ -21,6 +21,7 @@
 
 #include <QDebug>
 #include <QThread>
+#include <QPointer>
 
 #include "log.h"
 
@@ -31,18 +32,26 @@ static uint qHash(const \
Sink::ApplicationDomain::ApplicationDomainType &type)  return \
qHash(type.resourceInstanceIdentifier() + type.identifier());  }
 
+static qint64 getIdentifier(const QModelIndex &idx)
+{
+    if (!idx.isValid()) {
+        return 0;
+    }
+    return idx.internalId();
+}
+
 template <class T, class Ptr>
 ModelResult<T, Ptr>::ModelResult(const Sink::Query &query, const QList<QByteArray> \
                &propertyColumns, const Sink::Log::Context &ctx)
     : QAbstractItemModel(), mLogCtx(ctx.subContext("modelresult")), \
mPropertyColumns(propertyColumns), mQuery(query)  {
 }
 
-static qint64 getIdentifier(const QModelIndex &idx)
+template <class T, class Ptr>
+ModelResult<T, Ptr>::~ModelResult()
 {
-    if (!idx.isValid()) {
-        return 0;
+    if (mEmitter) {
+        mEmitter->waitForMethodExecutionEnd();
     }
-    return idx.internalId();
 }
 
 template <class T, class Ptr>
@@ -259,33 +268,41 @@ void ModelResult<T, Ptr>::setEmitter(const typename \
Sink::ResultEmitter<Ptr>::Pt  {
     setFetcher([this](const Ptr &parent) { mEmitter->fetch(parent); });
 
-    emitter->onAdded([this](const Ptr &value) {
+    QPointer<QObject> guard(this);
+    emitter->onAdded([this, guard](const Ptr &value) {
         SinkTraceCtx(mLogCtx) << "Received addition: " << value->identifier();
-        threadBoundary.callInMainThread([this, value]() {
+        Q_ASSERT(guard);
+        threadBoundary.callInMainThread([this, value, guard]() {
+            Q_ASSERT(guard);
             add(value);
         });
     });
-    emitter->onModified([this](const Ptr &value) {
+    emitter->onModified([this, guard](const Ptr &value) {
         SinkTraceCtx(mLogCtx) << "Received modification: " << value->identifier();
+        Q_ASSERT(guard);
         threadBoundary.callInMainThread([this, value]() {
             modify(value);
         });
     });
-    emitter->onRemoved([this](const Ptr &value) {
+    emitter->onRemoved([this, guard](const Ptr &value) {
         SinkTraceCtx(mLogCtx) << "Received removal: " << value->identifier();
+        Q_ASSERT(guard);
         threadBoundary.callInMainThread([this, value]() {
             remove(value);
         });
     });
-    emitter->onInitialResultSetComplete([this](const Ptr &parent, bool fetchedAll) {
+    emitter->onInitialResultSetComplete([this, guard](const Ptr &parent, bool \
                fetchedAll) {
         SinkTraceCtx(mLogCtx) << "Initial result set complete. Fetched all: " << \
                fetchedAll;
-        const qint64 parentId = parent ? qHash(*parent) : 0;
-        const auto parentIndex = createIndexFromId(parentId);
-        mEntityChildrenFetchComplete.insert(parentId);
-        if (fetchedAll) {
-            mEntityAllChildrenFetched.insert(parentId);
-        }
-        emit dataChanged(parentIndex, parentIndex, QVector<int>() << \
ChildrenFetchedRole); +        Q_ASSERT(guard);
+        threadBoundary.callInMainThread([=]() {
+            const qint64 parentId = parent ? qHash(*parent) : 0;
+            const auto parentIndex = createIndexFromId(parentId);
+            mEntityChildrenFetchComplete.insert(parentId);
+            if (fetchedAll) {
+                mEntityAllChildrenFetched.insert(parentId);
+            }
+            emit dataChanged(parentIndex, parentIndex, QVector<int>() << \
ChildrenFetchedRole); +        });
     });
     mEmitter = emitter;
 }
diff --git a/common/modelresult.h b/common/modelresult.h
index daa48bd..f30a8e1 100644
--- a/common/modelresult.h
+++ b/common/modelresult.h
@@ -42,6 +42,7 @@ public:
     };
 
     ModelResult(const Sink::Query &query, const QList<QByteArray> &propertyColumns, \
const Sink::Log::Context &); +    ~ModelResult();
 
     void setEmitter(const typename Sink::ResultEmitter<Ptr>::Ptr &);
 
diff --git a/common/resultprovider.h b/common/resultprovider.h
index cda4dac..86138db 100644
--- a/common/resultprovider.h
+++ b/common/resultprovider.h
@@ -22,6 +22,8 @@
 
 #include <functional>
 #include <memory>
+#include <QMutexLocker>
+#include <QPointer>
 
 namespace Sink {
 
@@ -82,21 +84,21 @@ public:
     void add(const T &value)
     {
         if (auto strongRef = mResultEmitter.toStrongRef()) {
-            strongRef->addHandler(value);
+            strongRef->add(value);
         }
     }
 
     void modify(const T &value)
     {
         if (auto strongRef = mResultEmitter.toStrongRef()) {
-            strongRef->modifyHandler(value);
+            strongRef->modify(value);
         }
     }
 
     void remove(const T &value)
     {
         if (auto strongRef = mResultEmitter.toStrongRef()) {
-            strongRef->removeHandler(value);
+            strongRef->remove(value);
         }
     }
 
@@ -194,6 +196,15 @@ public:
 
     virtual ~ResultEmitter()
     {
+        //Try locking in case we're in the middle of an execution in another thread
+        QMutexLocker locker{&mMutex};
+    }
+
+    virtual void waitForMethodExecutionEnd()
+    {
+        //If we're in the middle of a method execution, this will block until the \
method is done. +        QMutexLocker locker{&mMutex};
+        mDone = true;
     }
 
     void onAdded(const std::function<void(const DomainType &)> &handler)
@@ -226,38 +237,55 @@ public:
         clearHandler = handler;
     }
 
+    bool guardOk()
+    {
+        return !mDone;
+    }
+
     void add(const DomainType &value)
     {
-        addHandler(value);
+        QMutexLocker locker{&mMutex};
+        if (guardOk()) {
+            addHandler(value);
+        }
     }
 
     void modify(const DomainType &value)
     {
-        modifyHandler(value);
+        QMutexLocker locker{&mMutex};
+        if (guardOk()) {
+            modifyHandler(value);
+        }
     }
 
     void remove(const DomainType &value)
     {
-        removeHandler(value);
+        QMutexLocker locker{&mMutex};
+        if (guardOk()) {
+            removeHandler(value);
+        }
     }
 
     void initialResultSetComplete(const DomainType &parent, bool replayedAll)
     {
-        if (initialResultSetCompleteHandler) {
+        QMutexLocker locker{&mMutex};
+        if (initialResultSetCompleteHandler && guardOk()) {
             initialResultSetCompleteHandler(parent, replayedAll);
         }
     }
 
     void complete()
     {
-        if (completeHandler) {
+        QMutexLocker locker{&mMutex};
+        if (completeHandler && guardOk()) {
             completeHandler();
         }
     }
 
     void clear()
     {
-        if (clearHandler) {
+        QMutexLocker locker{&mMutex};
+        if (clearHandler && guardOk()) {
             clearHandler();
         }
     }
@@ -285,6 +313,8 @@ private:
     std::function<void(void)> clearHandler;
 
     std::function<void(const DomainType &parent)> mFetcher;
+    QMutex mMutex;
+    bool mDone = false;
 };
 
 template <class DomainType>
@@ -293,6 +323,18 @@ class AggregatingResultEmitter : public \
ResultEmitter<DomainType>  public:
     typedef QSharedPointer<AggregatingResultEmitter<DomainType>> Ptr;
 
+    ~AggregatingResultEmitter()
+    {
+    }
+
+    virtual void waitForMethodExecutionEnd() Q_DECL_OVERRIDE
+    {
+        for (const auto &emitter : mEmitter) {
+            emitter->waitForMethodExecutionEnd();
+        }
+        ResultEmitter<DomainType>::waitForMethodExecutionEnd();
+    }
+
     void addEmitter(const typename ResultEmitter<DomainType>::Ptr &emitter)
     {
         Q_ASSERT(emitter);


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic