File indexing completed on 2024-05-12 04:38:06
0001 /* 0002 SPDX-FileCopyrightText: 2008 David Nolden <david.nolden.kdevelop@art-master.de> 0003 0004 SPDX-License-Identifier: LGPL-2.0-only 0005 */ 0006 0007 #include "modificationrevisionset.h" 0008 #include <debug.h> 0009 0010 #include <serialization/itemrepository.h> 0011 #include <serialization/indexedstring.h> 0012 #include <util/setrepository.h> 0013 0014 //When uncommented, the reason for needed updates is printed 0015 // #define DEBUG_NEEDSUPDATE 0016 0017 namespace KDevelop { 0018 QRecursiveMutex* modificationRevisionSetMutex() 0019 { 0020 static QRecursiveMutex mutex; 0021 return &mutex; 0022 } 0023 0024 struct FileModificationPair 0025 { 0026 KDevelop::IndexedString file; 0027 KDevelop::ModificationRevision revision; 0028 0029 FileModificationPair() 0030 { 0031 } 0032 0033 FileModificationPair(const KDevelop::IndexedString& _file, KDevelop::ModificationRevision _revision) 0034 : file(_file) 0035 , revision(_revision) 0036 { 0037 } 0038 0039 FileModificationPair& operator=(const FileModificationPair& rhs) = delete; 0040 0041 unsigned int hash() const 0042 { 0043 return ((file.hash() + revision.modificationTime) * 17 + revision.revision) * 73; 0044 } 0045 0046 unsigned short int itemSize() const 0047 { 0048 return sizeof(FileModificationPair); 0049 } 0050 0051 bool operator==(const FileModificationPair& rhs) const 0052 { 0053 return file == rhs.file && revision == rhs.revision; 0054 } 0055 }; 0056 0057 struct FileModificationPairRequest 0058 { 0059 FileModificationPairRequest(const FileModificationPair& data) : m_data(data) 0060 { 0061 } 0062 0063 const FileModificationPair& m_data; 0064 0065 enum { 0066 AverageSize = sizeof(FileModificationPair) 0067 }; 0068 0069 unsigned int hash() const 0070 { 0071 return m_data.hash(); 0072 } 0073 0074 uint itemSize() const 0075 { 0076 return m_data.itemSize(); 0077 } 0078 0079 void createItem(FileModificationPair* item) const 0080 { 0081 new (item) FileModificationPair(m_data); 0082 } 0083 0084 bool equals(const FileModificationPair* item) const 0085 { 0086 return *item == m_data; 0087 } 0088 0089 static void destroy(FileModificationPair* item, KDevelop::AbstractItemRepository&) 0090 { 0091 item->~FileModificationPair(); 0092 } 0093 0094 static bool persistent(const FileModificationPair* /*item*/) 0095 { 0096 return true; //Reference-counting is done implicitly using the set-repository 0097 } 0098 }; 0099 0100 using FileModificationPairRepository 0101 = KDevelop::ItemRepository<FileModificationPair, FileModificationPairRequest, true, QRecursiveMutex>; 0102 0103 static FileModificationPairRepository& fileModificationPairRepository() 0104 { 0105 static FileModificationPairRepository rep(QStringLiteral("file modification repository"), 0106 modificationRevisionSetMutex()); 0107 return rep; 0108 } 0109 0110 void initModificationRevisionSetRepository() 0111 { 0112 fileModificationPairRepository(); 0113 } 0114 0115 QHash<uint, std::pair<QDateTime, bool>> needsUpdateCache; 0116 0117 void ModificationRevisionSet::clearCache() 0118 { 0119 QMutexLocker lock(modificationRevisionSetMutex()); 0120 ///@todo More intelligent clearing. We actually need to watch the directory for changes, and if there are changes, clear the cache. 0121 needsUpdateCache.clear(); 0122 } 0123 0124 struct FileModificationSetRepository 0125 : public Utils::BasicSetRepository 0126 { 0127 FileModificationSetRepository() 0128 : Utils::BasicSetRepository(QStringLiteral("file modification sets"), modificationRevisionSetMutex(), 0129 &globalItemRepositoryRegistry(), true) 0130 { 0131 } 0132 void itemRemovedFromSets(uint index) override; 0133 }; 0134 0135 struct FileModificationSetRepositoryRepresenter 0136 { 0137 static FileModificationSetRepository& repository() 0138 { 0139 static FileModificationSetRepository fileModificationSetRepository; 0140 return fileModificationSetRepository; 0141 } 0142 }; 0143 0144 ModificationRevisionSet::ModificationRevisionSet(unsigned int index) : m_index(index) 0145 { 0146 } 0147 0148 uint ModificationRevisionSet::size() const 0149 { 0150 Utils::Set set = Utils::Set(m_index, &FileModificationSetRepositoryRepresenter::repository()); 0151 return set.count(); 0152 } 0153 0154 void ModificationRevisionSet::clear() 0155 { 0156 QMutexLocker lock(modificationRevisionSetMutex()); 0157 0158 if (m_index) { 0159 Utils::Set oldModificationTimes = Utils::Set(m_index, &FileModificationSetRepositoryRepresenter::repository()); 0160 oldModificationTimes.staticUnref(); 0161 m_index = 0; 0162 } 0163 } 0164 0165 void ModificationRevisionSet::addModificationRevision(const IndexedString& url, 0166 const KDevelop::ModificationRevision& revision) 0167 { 0168 QMutexLocker lock(modificationRevisionSetMutex()); 0169 0170 if (m_index == 0) { 0171 Utils::Set set = FileModificationSetRepositoryRepresenter::repository().createSet( 0172 fileModificationPairRepository().index(FileModificationPair(url, revision))); 0173 set.staticRef(); 0174 m_index = set.setIndex(); 0175 } else { 0176 Utils::Set oldModificationTimes = Utils::Set(m_index, &FileModificationSetRepositoryRepresenter::repository()); 0177 Utils::Set newModificationTimes = oldModificationTimes; 0178 0179 Utils::Set tempSet = FileModificationSetRepositoryRepresenter::repository().createSet( 0180 fileModificationPairRepository().index(FileModificationPair(url, revision))); 0181 tempSet.staticRef(); 0182 0183 newModificationTimes += tempSet; 0184 newModificationTimes.staticRef(); 0185 oldModificationTimes.staticUnref(); 0186 tempSet.staticUnref(); 0187 0188 m_index = newModificationTimes.setIndex(); 0189 } 0190 } 0191 0192 bool ModificationRevisionSet::removeModificationRevision(const IndexedString& url, 0193 const KDevelop::ModificationRevision& revision) 0194 { 0195 QMutexLocker lock(modificationRevisionSetMutex()); 0196 0197 if (!m_index) 0198 return false; 0199 0200 Utils::Set oldModificationTimes = Utils::Set(m_index, &FileModificationSetRepositoryRepresenter::repository()); 0201 Utils::Set newModificationTimes = oldModificationTimes; 0202 0203 Utils::Set tempSet = FileModificationSetRepositoryRepresenter::repository().createSet( 0204 fileModificationPairRepository().index(FileModificationPair(url, revision))); 0205 tempSet.staticRef(); 0206 0207 newModificationTimes -= tempSet; 0208 newModificationTimes.staticRef(); 0209 oldModificationTimes.staticUnref(); 0210 tempSet.staticUnref(); 0211 0212 m_index = newModificationTimes.setIndex(); 0213 return m_index != oldModificationTimes.setIndex(); 0214 } 0215 0216 // const QMap<IndexedString, KDevelop::ModificationRevision> ModificationRevisionSet::allModificationTimes() const { 0217 // QMap<IndexedString, KDevelop::ModificationRevision> ret; 0218 // Utils::Set::Iterator it = m_allModificationTimes.iterator(); 0219 // while(it) { 0220 // const FileModificationPair* data = fileModificationPairRepository().itemFromIndex(*it); 0221 // ret[data->file] = data->revision; 0222 // ++it; 0223 // } 0224 // return ret; 0225 // } 0226 0227 using ModificationRevisionSetNode = Utils::VirtualSetNode<uint, Utils::IdentityConversion<uint>, 0228 FileModificationSetRepositoryRepresenter>; 0229 // static bool (const Utils::SetNodeData* node) { 0230 // ModificationRevisionSetNode 0231 // if(!node) 0232 // return false; 0233 // } 0234 0235 static bool nodeNeedsUpdate(uint index) 0236 { 0237 QMutexLocker lock(modificationRevisionSetMutex()); 0238 0239 if (!index) 0240 return false; 0241 0242 const auto currentTime = QDateTime::currentDateTimeUtc(); 0243 0244 auto cached = needsUpdateCache.constFind(index); 0245 if (cached != needsUpdateCache.constEnd()) { 0246 if ((*cached).first.secsTo(currentTime) < cacheModificationTimesForSeconds) { 0247 return cached->second; 0248 } 0249 } 0250 0251 bool result = false; 0252 0253 const Utils::SetNodeData* nodeData = FileModificationSetRepositoryRepresenter::repository().nodeFromIndex(index); 0254 if (nodeData->contiguous()) { 0255 //Do the actual checking 0256 for (unsigned int a = nodeData->start(); a < nodeData->end(); ++a) { 0257 const FileModificationPair* data = fileModificationPairRepository().itemFromIndex(a); 0258 ModificationRevision revision = KDevelop::ModificationRevision::revisionForFile(data->file); 0259 if (revision != data->revision) { 0260 result = true; 0261 break; 0262 } 0263 } 0264 } else { 0265 result = nodeNeedsUpdate(nodeData->leftNode()) || nodeNeedsUpdate(nodeData->rightNode()); 0266 } 0267 0268 needsUpdateCache.insert(index, std::make_pair(currentTime, result)); 0269 0270 return result; 0271 } 0272 0273 QString ModificationRevisionSet::toString() const 0274 { 0275 QMutexLocker lock(modificationRevisionSetMutex()); 0276 Utils::Set set(m_index, &FileModificationSetRepositoryRepresenter::repository()); 0277 Utils::Set::Iterator it = set.iterator(); 0278 QStringList revisions; 0279 while (it) { 0280 const FileModificationPair* data = fileModificationPairRepository().itemFromIndex(*it); 0281 revisions.append(data->file.str() + QLatin1Char(':') + data->revision.toString()); 0282 ++it; 0283 } 0284 0285 QString ret = QLatin1Char('[') + revisions.join(QLatin1String(", ")) + QLatin1Char(']'); 0286 return ret; 0287 } 0288 0289 bool ModificationRevisionSet::needsUpdate() const 0290 { 0291 QMutexLocker lock(modificationRevisionSetMutex()); 0292 0293 #ifdef DEBUG_NEEDSUPDATE 0294 Utils::Set set(m_index, &FileModificationSetRepositoryRepresenter::repository()); 0295 Utils::Set::Iterator it = set.iterator(); 0296 while (it) { 0297 const FileModificationPair* data = fileModificationPairRepository().itemFromIndex(*it); 0298 ModificationRevision revision = KDevelop::ModificationRevision::revisionForFile(data->file); 0299 if (revision != data->revision) { 0300 qCDebug(LANGUAGE) << "dependency" << data->file.str() << "has changed, stored stamp:" << data->revision << 0301 "new time:" << revision; 0302 return true; 0303 } 0304 ++it; 0305 } 0306 return false; 0307 #else 0308 return nodeNeedsUpdate(m_index); 0309 #endif 0310 } 0311 0312 ModificationRevisionSet& ModificationRevisionSet::operator+=(const ModificationRevisionSet& rhs) 0313 { 0314 QMutexLocker lock(modificationRevisionSetMutex()); 0315 0316 Utils::Set oldModificationTimes = Utils::Set(m_index, &FileModificationSetRepositoryRepresenter::repository()); 0317 Utils::Set otherModificationTimes = 0318 Utils::Set(rhs.m_index, &FileModificationSetRepositoryRepresenter::repository()); 0319 0320 Utils::Set newModificationTimes = oldModificationTimes; 0321 0322 newModificationTimes += otherModificationTimes; 0323 newModificationTimes.staticRef(); 0324 oldModificationTimes.staticUnref(); 0325 0326 m_index = newModificationTimes.setIndex(); 0327 0328 return *this; 0329 } 0330 0331 ModificationRevisionSet& ModificationRevisionSet::operator-=(const ModificationRevisionSet& rhs) 0332 { 0333 QMutexLocker lock(modificationRevisionSetMutex()); 0334 0335 Utils::Set oldModificationTimes = Utils::Set(m_index, &FileModificationSetRepositoryRepresenter::repository()); 0336 Utils::Set otherModificationTimes = 0337 Utils::Set(rhs.m_index, &FileModificationSetRepositoryRepresenter::repository()); 0338 0339 Utils::Set newModificationTimes = oldModificationTimes; 0340 0341 newModificationTimes -= otherModificationTimes; 0342 newModificationTimes.staticRef(); 0343 oldModificationTimes.staticUnref(); 0344 0345 m_index = newModificationTimes.setIndex(); 0346 0347 return *this; 0348 } 0349 0350 void FileModificationSetRepository::itemRemovedFromSets(uint index) 0351 { 0352 fileModificationPairRepository().deleteItem(index); 0353 needsUpdateCache.remove(index); 0354 } 0355 }