File workaround-hang-on-exit.patch of Package kdevelop5

diff --git a/kdevplatform/language/duchain/duchain.cpp b/kdevplatform/language/duchain/duchain.cpp
index d9d1700ddf8dd2cd18b838a48c5b0a9841acf69c..d92bdf2ed0000e3e468a7d462389ae5c6941eada 100644
--- a/kdevplatform/language/duchain/duchain.cpp
+++ b/kdevplatform/language/duchain/duchain.cpp
@@ -433,7 +433,18 @@ public:
 
         // qCDebug(LANGUAGE) << "duchain: removing document" << context->url().str();
         Q_ASSERT(hasChainForIndex(index));
+        if (!hasChainForIndex(index)) {
+            qCCritical(LANGUAGE) << Q_FUNC_INFO << "index" << index << "has no chain";
+            return;
+        }
         Q_ASSERT(m_chainsByUrl.contains(context->url(), context));
+        if (!m_chainsByUrl.contains(context->url(), context)) {
+            qCCritical(LANGUAGE) << Q_FUNC_INFO << "Context" << context << "has no registered chainByUrl";
+            if (context) {
+                qCritical() << "\t" << "url=" << context->url();
+            }
+            return;
+        }
 
         m_chainsByUrl.remove(context->url(), context);
 
@@ -446,6 +457,10 @@ public:
         l.relock();
 
         Q_ASSERT(hasChainForIndex(index));
+        if (!hasChainForIndex(index)) {
+            qCCritical(LANGUAGE) << Q_FUNC_INFO << "index" << index << "has no chain";
+            return;
+        }
 
         QMutexLocker lock(&DUChain::chainsByIndexLock);
         DUChain::chainsByIndex[index] = nullptr;
@@ -768,7 +783,7 @@ public:
             //Here we wait for all parsing-threads to stop their processing
             for (const auto language : qAsConst(languages)) {
                 if (lockFlag == TryLock) {
-                    if (!language->parseLock()->tryLockForWrite()) {
+                    if (!language->parseLock() || !language->parseLock()->tryLockForWrite()) {
                         qCDebug(LANGUAGE) << "Aborting cleanup because language plugin is still parsing:" <<
                             language->name();
                         // some language is still parsing, don't interfere with the cleanup
@@ -1587,8 +1602,9 @@ void DUChain::documentClosed(IDocument* document)
 
     const auto currentDocumentContexts = sdDUChainPrivate->m_openDocumentContexts;
     for (const ReferencedTopDUContext& top : currentDocumentContexts) {
-        if (top->url() == url)
+        if (!top || (sdDUChainPrivate->m_referenceCounts[top] == 0 || top->url() == url)) {
             sdDUChainPrivate->m_openDocumentContexts.remove(top);
+        }
     }
 }
 
@@ -1688,11 +1704,16 @@ static void finalCleanup()
     qCDebug(LANGUAGE) << "doing final cleanup";
 
     int cleaned = 0;
+    int passes = 0;
     while ((cleaned = globalItemRepositoryRegistry().finalCleanup())) {
+        passes += 1;
         qCDebug(LANGUAGE) << "cleaned" << cleaned << "B";
         if (cleaned < 1000) {
             qCDebug(LANGUAGE) << "cleaned enough";
             break;
+        } else if (passes >= 100) {
+            qCWarning(LANGUAGE) << "cleaned" << passes << "passes, last was" << cleaned << "B; that's enough";
+            break;
         }
     }
     qCDebug(LANGUAGE) << "final cleanup ready";
diff --git a/kdevplatform/serialization/itemrepository.h b/kdevplatform/serialization/itemrepository.h
index f8c2c58307..8a6179bbd4 100644
--- a/kdevplatform/serialization/itemrepository.h
+++ b/kdevplatform/serialization/itemrepository.h
@@ -508,7 +508,7 @@ class Bucket {
     }
 
     template <class Repository>
-    void deleteItem(unsigned short index, unsigned int hash, Repository& repository)
+    bool deleteItem(unsigned short index, unsigned int hash, Repository& repository)
     {
         ifDebugLostSpace(Q_ASSERT(!lostSpace()); )
 
@@ -527,8 +527,23 @@ class Bucket {
             currentIndex = followerIndex(currentIndex);
             //If this assertion triggers, the deleted item was not registered under the given hash
             Q_ASSERT(currentIndex);
+            if (!currentIndex) {
+                qWarning() << QStringLiteral("Bucket::deleteItem(%1,%2,%3)").arg(index).arg(hash).arg(repository.repositoryName())
+                    << ": early return because currentIndex==0";
+                return false;
+            } else if (currentIndex == previousIndex) {
+                qWarning() << QStringLiteral("Bucket::deleteItem(%1,%2,%3)").arg(index).arg(hash).arg(repository.repositoryName())
+                    << "early return because currentIndex==previousIndex==" << currentIndex;
+                return false;
+            }
         }
         Q_ASSERT(currentIndex == index);
+        if (currentIndex != index) {
+            qWarning() << QStringLiteral("Bucket::deleteItem(%1,%2,%3)").arg(index).arg(hash).arg(repository.repositoryName())
+                << "early return because currentIndex != index ("
+                << currentIndex << "!=" << index;
+            return false;
+        }
 
         if (!previousIndex)
             //The item was directly in the object map
@@ -594,6 +609,7 @@ class Bucket {
         }
 #endif
 //       Q_ASSERT(canAllocateItem(size));
+        return true;
     }
 
     ///@warning The returned item may be in write-protected memory, so never try doing a const_cast and changing some data
@@ -655,9 +671,10 @@ class Bucket {
     int finalCleanup(Repository& repository)
     {
         int changed = 0;
+        bool dirty = m_dirty;
 
-        while (m_dirty) {
-            m_dirty = false;
+        while (dirty) {
+            dirty = false;
 
             for (uint a = 0; a < ObjectMapSize; ++a) {
                 uint currentIndex = m_objectMap[a];
@@ -669,15 +686,24 @@ class Bucket {
 
                     if (!ItemRequest::persistent(item)) {
                         changed += item->itemSize();
-                        deleteItem(currentIndex, item->hash(), repository);
-                        m_dirty = true; //Set to dirty so we re-iterate
-                        break;
+                        if (deleteItem(currentIndex, item->hash(), repository)) {
+                            dirty = true; //Set to dirty so we re-iterate
+                            break;
+                        } else {
+                            qWarning() << "\tdidn't delete item of size" << item->itemSize();
+                        }
                     }
 
+                    uint prevIndex = currentIndex;
                     currentIndex = followerIndex(currentIndex);
+                    if (currentIndex == prevIndex) {
+                        qWarning() << Q_FUNC_INFO << "avoided deadloop in repository" << repository.repositoryName();
+                        currentIndex = 0;
+                    }
                 }
             }
         }
+        m_dirty = dirty;
         return changed;
     }
 
@@ -947,14 +973,23 @@ class Bucket {
     /// @param index the index of an item @return The index of the next item in the chain of items with a same local hash, or zero
     inline unsigned short followerIndex(unsigned short index) const
     {
-        Q_ASSERT(index >= 2);
-        return *reinterpret_cast<unsigned short*>(m_data + (index - 2));
+//        Q_ASSERT(index >= 2);
+        if (index >= 2) {
+            return *reinterpret_cast<unsigned short*>(m_data + (index - 2));
+        } else {
+            qWarning() << Q_FUNC_INFO << "index=" << index << "; returning 0";
+            return 0;
+        }
     }
 
     void setFollowerIndex(unsigned short index, unsigned short follower)
     {
-        Q_ASSERT(index >= 2);
-        *reinterpret_cast<unsigned short*>(m_data + (index - 2)) = follower;
+//        Q_ASSERT(index >= 2);
+        if (index >= 2) {
+            *reinterpret_cast<unsigned short*>(m_data + (index - 2)) = follower;
+        } else {
+            qWarning() << Q_FUNC_INFO << "index=" << index << "; follower set to 0 instead of" << follower;
+        }
     }
     // Only returns the current value if the item is actually free
     inline unsigned short freeSize(unsigned short index) const
@@ -1404,6 +1439,16 @@ class ItemRepository : public AbstractItemRepository {
 
         //Make sure the index was reachable through the hash chain
         Q_ASSERT(bucketPtr);
+        if (!bucketPtr) {
+            qWarning() << Q_FUNC_INFO << "index" << index
+                << "not reachable through hash chain; previous bucket="
+                << previousBucketPtr << "next="
+                << (previousBucketPtr ? previousBucketPtr->nextBucketForHash(hash) : 0);
+            if (previousBucketPtr) {
+                previousBucketPtr->setNextBucketForHash(hash, 0);
+            }
+            return;
+        }
 
         --m_statItemCount;
 
openSUSE Build Service is sponsored by