File indexing completed on 2024-05-05 04:38:10

0001 /*
0002     SPDX-FileCopyrightText: 2009 David Nolden <david.nolden.kdevelop@art-master.de>
0003     SPDX-FileCopyrightText: 2020 Igor Kushnir <igorkuo@gmail.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "referencecounting.h"
0009 #include "itemrepository.h"
0010 
0011 #include <QAtomicInt>
0012 
0013 #include <algorithm>
0014 
0015 namespace KDevelop {
0016 void DUChainReferenceCounting::Interval::assign(Pointer newStart, unsigned newSize) noexcept
0017 {
0018     start = newStart;
0019     size = newSize;
0020     refCount = 1;
0021 }
0022 
0023 auto DUChainReferenceCounting::findInterval(Pointer start, unsigned size) noexcept -> Interval*
0024 {
0025     return std::find_if(intervals, intervals + count, [start, size](Interval interval) {
0026         return interval.start == start && interval.size == size;
0027     });
0028 }
0029 
0030 void DUChainReferenceCounting::enable(Pointer start, unsigned size)
0031 {
0032     auto* const interval = findInterval(start, size);
0033     if (interval == intervals + count) {
0034         if (count == maxIntervalCount) {
0035             qFatal("DUChainReferenceCounting interval count limit of %zu exceeded!", count);
0036         }
0037         // "push_back"
0038         Q_ASSERT(count < maxIntervalCount);
0039         interval->assign(start, size);
0040         ++count;
0041     } else {
0042         Q_ASSERT(interval < intervals + count);
0043         ++interval->refCount;
0044     }
0045 
0046 #ifdef TEST_REFERENCE_COUNTING
0047     Q_ASSERT(shouldDo(start));
0048     Q_ASSERT(shouldDo(start + size - 1));
0049 #endif
0050 }
0051 
0052 void DUChainReferenceCounting::disable(Pointer start, unsigned size)
0053 {
0054     auto* const interval = findInterval(start, size);
0055     Q_ASSERT(interval < intervals + count);
0056 
0057     if (interval->refCount == 1) {
0058         // "erase" interval
0059         std::move(interval + 1, intervals + count, interval);
0060         --count;
0061     } else {
0062         Q_ASSERT(interval->refCount > 1);
0063         --interval->refCount;
0064     }
0065 }
0066 
0067 void enableDUChainReferenceCounting(const void* start, unsigned size)
0068 {
0069     DUChainReferenceCounting::instance().enable(reinterpret_cast<DUChainReferenceCounting::Pointer>(start), size);
0070 }
0071 
0072 void disableDUChainReferenceCounting(const void* start, unsigned size)
0073 {
0074     DUChainReferenceCounting::instance().disable(reinterpret_cast<DUChainReferenceCounting::Pointer>(start), size);
0075 }
0076 }
0077 
0078 #ifdef TEST_REFERENCE_COUNTING
0079 
0080 QAtomicInt& id()
0081 {
0082     static QAtomicInt& ret(KDevelop::globalItemRepositoryRegistry().getCustomCounter("referencer ids", 1));
0083     return ret;
0084 }
0085 
0086 namespace KDevelop {
0087 ReferenceCountManager::ReferenceCountManager() : m_id(id().fetchAndAddRelaxed(1))
0088 {
0089 }
0090 
0091 struct ReferenceCountItem
0092 {
0093     ///Item entries:
0094     ReferenceCountItem(uint id, uint target) : m_id(id)
0095         , m_targetId(target)
0096     {
0097     }
0098 
0099     ReferenceCountItem& operator=(const ReferenceCountItem& rhs) = delete;
0100 
0101     //Every item has to implement this function, and return a valid hash.
0102     //Must be exactly the same hash value as ReferenceCountItemRequest::hash() has returned while creating the item.
0103     unsigned int hash() const
0104     {
0105         return KDevHash() << m_id << m_targetId;
0106     }
0107 
0108     //Every item has to implement this function, and return the complete size this item takes in memory.
0109     //Must be exactly the same value as ReferenceCountItemRequest::itemSize() has returned while creating the item.
0110     unsigned short int itemSize() const
0111     {
0112         return sizeof(ReferenceCountItem);
0113     }
0114 
0115     uint m_id;
0116     uint m_targetId;
0117 
0118     ///Request entries:
0119     enum {
0120         AverageSize = 8
0121     };
0122 
0123     void createItem(ReferenceCountItem* item) const
0124     {
0125         * item = *this;
0126     }
0127     static void destroy(ReferenceCountItem* /*item*/, AbstractItemRepository&)
0128     {
0129     }
0130 
0131     static bool persistent(const ReferenceCountItem*)
0132     {
0133         return true;
0134     }
0135 
0136     bool equals(const ReferenceCountItem* item) const
0137     {
0138         return m_id == item->m_id && m_targetId == item->m_targetId;
0139     }
0140 };
0141 
0142 static RepositoryManager<ItemRepository<ReferenceCountItem, ReferenceCountItem, false, true,
0143         sizeof(ReferenceCountItem)>, false>& references()
0144 {
0145     static RepositoryManager<ItemRepository<ReferenceCountItem, ReferenceCountItem, false, true,
0146             sizeof(ReferenceCountItem)>, false> referencesObject("Reference Count Debugging");
0147     return referencesObject;
0148 }
0149 static RepositoryManager<ItemRepository<ReferenceCountItem, ReferenceCountItem, false, true,
0150         sizeof(ReferenceCountItem)>, false>& oldReferences()
0151 {
0152     static RepositoryManager<ItemRepository<ReferenceCountItem, ReferenceCountItem, false, true,
0153             sizeof(ReferenceCountItem)>, false> oldReferencesObject("Old Reference Count Debugging");
0154     return oldReferencesObject;
0155 }
0156 
0157 void KDevelop::initReferenceCounting()
0158 {
0159     references();
0160     oldReferences();
0161 }
0162 
0163 ReferenceCountManager::~ReferenceCountManager()
0164 {
0165     //Make sure everything is cleaned up when the object is destroyed
0166 //   Q_ASSERT(!references().contains(m_id));
0167 }
0168 
0169 ReferenceCountManager::ReferenceCountManager(const ReferenceCountManager& rhs) : m_id(id().fetchAndAddRelaxed(1))
0170 {
0171     //New id
0172 }
0173 
0174 ReferenceCountManager& ReferenceCountManager::ReferenceCountManager::operator=(const ReferenceCountManager& rhs)
0175 {
0176     //Keep id
0177     return *this;
0178 }
0179 
0180 // bool ReferenceCountManager::hasReferenceCount() const {
0181 //   return references->findIndex(ReferenceCountItem);
0182 // }
0183 
0184 void ReferenceCountManager::increase(uint& ref, uint targetId)
0185 {
0186     Q_ASSERT(shouldDoDUChainReferenceCounting(this));
0187     Q_ASSERT(!references->findIndex(ReferenceCountItem(m_id, targetId)));
0188     ++ref;
0189 
0190     {
0191         int oldIndex = oldReferences->findIndex(ReferenceCountItem(m_id, targetId));
0192         if (oldIndex)
0193             oldReferences->deleteItem(oldIndex);
0194     }
0195 
0196     Q_ASSERT(references->index(ReferenceCountItem(m_id, targetId)));
0197 }
0198 
0199 void ReferenceCountManager::decrease(uint& ref, uint targetId)
0200 {
0201     Q_ASSERT(ref > 0);
0202     Q_ASSERT(shouldDoDUChainReferenceCounting(this));
0203     Q_ASSERT(!oldReferences->findIndex(ReferenceCountItem(m_id, targetId)));
0204     uint refIndex = references->findIndex(ReferenceCountItem(m_id, targetId));
0205     Q_ASSERT(refIndex);
0206     --ref;
0207     references->deleteItem(refIndex);
0208     oldReferences->index(ReferenceCountItem(m_id, targetId));
0209 }
0210 }
0211 
0212 #else
0213 namespace KDevelop {
0214 void initReferenceCounting()
0215 {
0216 }
0217 }
0218 #endif