• Skip to content
  • Skip to link menu
KDE 4.4 API Reference
  • KDE API Reference
  • KDevelop Platform Libraries
  • Sitemap
  • Contact Us
 

language/duchain

duchain.cpp

00001 /* This  is part of KDevelop
00002     Copyright 2006-2008 Hamish Rodda <rodda@kde.org>
00003     Copyright 2007-2008 David Nolden <david.nolden.kdevelop@art-master.de>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License version 2 as published by the Free Software Foundation.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017    Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include "duchain.h"
00021 #include "duchainlock.h"
00022 
00023 #include <QtCore/QCoreApplication>
00024 #include <QApplication>
00025 #include <QtCore/QHash>
00026 #include <QtCore/QMultiMap>
00027 #include <QtCore/QTimer>
00028 #include <QtCore/QReadWriteLock>
00029 #include <QtCore/qatomic.h>
00030 
00031 #include <kglobal.h>
00032 
00033 #include <KDE/KTextEditor/Document>
00034 #include <KDE/KTextEditor/SmartInterface>
00035 
00036 #include <interfaces/idocumentcontroller.h>
00037 #include <interfaces/icore.h>
00038 #include <interfaces/ilanguage.h>
00039 #include <interfaces/ilanguagecontroller.h>
00040 
00041 #include "../editor/editorintegrator.h"
00042 #include "../interfaces/ilanguagesupport.h"
00043 #include "../interfaces/icodehighlighting.h"
00044 #include "../backgroundparser/backgroundparser.h"
00045 
00046 #include "topducontext.h"
00047 #include "topducontextdata.h"
00048 #include "topducontextdynamicdata.h"
00049 #include "parsingenvironment.h"
00050 #include "declaration.h"
00051 #include "definitions.h"
00052 #include "duchainutils.h"
00053 #include "use.h"
00054 #include "uses.h"
00055 #include "abstractfunctiondeclaration.h"
00056 #include "smartconverter.h"
00057 #include "duchainregister.h"
00058 #include "persistentsymboltable.h"
00059 #include "repositories/itemrepository.h"
00060 #include <util/google/dense_hash_map>
00061 #include <QtCore/qthread.h>
00062 #include <QtCore/qwaitcondition.h>
00063 #include <QtCore/qmutex.h>
00064 #include <unistd.h>
00065 #include "waitforupdate.h"
00066 #include "referencecounting.h"
00067 
00068 Q_DECLARE_METATYPE(KDevelop::IndexedString)
00069 Q_DECLARE_METATYPE(KDevelop::IndexedTopDUContext)
00070 Q_DECLARE_METATYPE(KDevelop::ReferencedTopDUContext)
00071 
00072 namespace {
00073 //Additional "soft" cleanup steps that are done before the actual cleanup.
00074 //During "soft" cleanup, the consistency is not guaranteed. The repository is
00075 //marked to be updating during soft cleanup, so if kdevelop crashes, it will be cleared.
00076 //The big advantage of the soft cleanup steps is, that the duchain is always only locked for
00077 //short times, which leads to no lockup in the UI.
00078 const int SOFT_CLEANUP_STEPS = 1;
00079 
00080 const uint cleanupEverySeconds = 200;
00081 
00083 const uint maxFinalCleanupCheckContexts = 2000;
00084 const uint minimumFinalCleanupCheckContextsPercentage = 10; //Check at least n% of all top-contexts during cleanup
00085 //Set to true as soon as the duchain is deleted
00086 bool duChainDeleted = false;
00087 }
00088 
00089 namespace KDevelop
00090 {
00091 //This thing is not actually used, but it's needed for compiling
00092 DEFINE_LIST_MEMBER_HASH(EnvironmentInformationListItem, items, uint)
00093 
00094 //An entry for the item-repository that holds some meta-data. Behind this entry, the actual ParsingEnvironmentFileData is stored.
00095 class EnvironmentInformationItem {
00096   public:
00097   EnvironmentInformationItem(uint topContext, uint size) : m_topContext(topContext), m_size(size) {
00098   }
00099 
00100   ~EnvironmentInformationItem() {
00101   }
00102 
00103   unsigned int hash() const {
00104     return m_topContext;
00105   }
00106 
00107   unsigned int itemSize() const {
00108     return sizeof(*this) + m_size;
00109   }
00110 
00111   uint m_topContext;
00112   uint m_size;//Size of the data behind, that holds the actual item
00113 };
00114 
00115 struct ItemRepositoryIndexHash
00116 {
00117   size_t
00118   operator()(unsigned int __x) const
00119   { return 173*(__x>>2) + 11 * (__x >> 16); }
00120 };
00121 
00122 
00123 class EnvironmentInformationRequest {
00124   public:
00125 
00127   EnvironmentInformationRequest(uint topContextIndex) : m_file(0), m_index(topContextIndex) {
00128   }
00129 
00130   EnvironmentInformationRequest(const ParsingEnvironmentFile* file) : m_file(file), m_index(file->indexedTopContext().index()) {
00131   }
00132 
00133   enum {
00134     AverageSize = 32 //This should be the approximate average size of an Item
00135   };
00136 
00137   unsigned int hash() const {
00138     return m_index;
00139   }
00140 
00141   size_t itemSize() const {
00142     return sizeof(EnvironmentInformationItem) + DUChainItemSystem::self().dynamicSize(*m_file->d_func());
00143   }
00144 
00145   void createItem(EnvironmentInformationItem* item) const {
00146     new (item) EnvironmentInformationItem(m_index, DUChainItemSystem::self().dynamicSize(*m_file->d_func()));
00147     Q_ASSERT(m_file->d_func()->m_dynamic);
00148     DUChainItemSystem::self().copy(*m_file->d_func(), *(DUChainBaseData*)(((char*)item) + sizeof(EnvironmentInformationItem)), true);
00149     Q_ASSERT((*(DUChainBaseData*)(((char*)item) + sizeof(EnvironmentInformationItem))).m_range == m_file->d_func()->m_range);
00150     Q_ASSERT((*(DUChainBaseData*)(((char*)item) + sizeof(EnvironmentInformationItem))).classId == m_file->d_func()->classId);
00151     Q_ASSERT((*(DUChainBaseData*)(((char*)item) + sizeof(EnvironmentInformationItem))).m_dynamic == false);
00152   }
00153   
00154   static void destroy(EnvironmentInformationItem* item, KDevelop::AbstractItemRepository&) {
00155     item->~EnvironmentInformationItem();
00156     //We don't need to call the destructor, because that's done in DUChainBase::makeDynamic()
00157     //We just need to make sure that every environment-file is dynamic when it's deleted
00158 //     DUChainItemSystem::self().callDestructor((DUChainBaseData*)(((char*)item) + sizeof(EnvironmentInformationItem)));
00159   }
00160   
00161   static bool persistent(const EnvironmentInformationItem* ) {
00162     //Cleanup done separately
00163     return true;
00164   }
00165 
00166   bool equals(const EnvironmentInformationItem* item) const {
00167     return m_index == item->m_topContext;
00168   }
00169 
00170   const ParsingEnvironmentFile* m_file;
00171   uint m_index;
00172 };
00173 
00175 class EnvironmentInformationListItem {
00176   public:
00177   EnvironmentInformationListItem() {
00178     initializeAppendedLists(true);
00179   }
00180   
00181   EnvironmentInformationListItem(const EnvironmentInformationListItem& rhs, bool dynamic = true) {
00182     initializeAppendedLists(dynamic);
00183     m_file = rhs.m_file;
00184     copyListsFrom(rhs);
00185   }
00186   
00187   ~EnvironmentInformationListItem() {
00188     freeAppendedLists();
00189   }
00190 
00191   unsigned int hash() const {
00192     //We only compare the declaration. This allows us implementing a map, although the item-repository
00193     //originally represents a set.
00194     return m_file.hash();
00195   }
00196 
00197   unsigned short int itemSize() const {
00198     return dynamicSize();
00199   }
00200 
00201   IndexedString m_file;
00202 
00203   uint classSize() const {
00204     return sizeof(*this);
00205   }
00206 
00207   START_APPENDED_LISTS(EnvironmentInformationListItem);
00209   APPENDED_LIST_FIRST(EnvironmentInformationListItem, uint, items);
00210   END_APPENDED_LISTS(EnvironmentInformationListItem, items);
00211 };
00212 
00213 class EnvironmentInformationListRequest {
00214   public:
00215 
00217   EnvironmentInformationListRequest(const IndexedString& file) : m_file(file), m_item(0) {
00218   }
00220   EnvironmentInformationListRequest(const IndexedString& file, const EnvironmentInformationListItem& item) : m_file(file), m_item(&item) {
00221   }
00222 
00223   enum {
00224     AverageSize = 160 //This should be the approximate average size of an Item
00225   };
00226 
00227   unsigned int hash() const {
00228     return m_file.hash();
00229   }
00230 
00231   size_t itemSize() const {
00232     return m_item->itemSize();
00233   }
00234 
00235   void createItem(EnvironmentInformationListItem* item) const {
00236     Q_ASSERT(m_item->m_file == m_file);
00237     new (item) EnvironmentInformationListItem(*m_item, false);
00238   }
00239   
00240   static void destroy(EnvironmentInformationListItem* item, KDevelop::AbstractItemRepository&) {
00241     item->~EnvironmentInformationListItem();
00242   }
00243 
00244   static bool persistent(const EnvironmentInformationListItem*) {
00245     //Cleanup is done separately
00246     return true;
00247   }
00248 
00249   bool equals(const EnvironmentInformationListItem* item) const {
00250     return m_file == item->m_file;
00251   }
00252 
00253   IndexedString m_file;
00254   const EnvironmentInformationListItem* m_item;
00255 };
00256 
00257 class DUChainPrivate;
00258 static DUChainPrivate* duChainPrivateSelf = 0;
00259 class DUChainPrivate
00260 {
00261   class CleanupThread : public QThread {
00262     public:
00263       CleanupThread(DUChainPrivate* data) : m_stopRunning(false), m_data(data) {
00264       }
00265 
00266       void stopThread() {
00267         m_waitMutex.lock();
00268         m_stopRunning = true;
00269         m_wait.wakeAll(); //Wakes the thread up, so it notices it should exit
00270         m_waitMutex.unlock();
00271         wait();
00272       }
00273 
00274     private:
00275       void run() {
00276         while(1) {
00277           m_waitMutex.lock();
00278           if(m_stopRunning)
00279             break;
00280           m_wait.wait(&m_waitMutex, 1000 * cleanupEverySeconds);
00281           m_waitMutex.unlock();
00282           if(m_stopRunning)
00283             break;
00284 
00285           //Just to make sure the cache is cleared periodically
00286           ModificationRevisionSet::clearCache();
00287 
00288           m_data->doMoreCleanup(SOFT_CLEANUP_STEPS);
00289           if(m_stopRunning)
00290             break;
00291         }
00292       }
00293       bool m_stopRunning;
00294       QWaitCondition m_wait;
00295       QMutex m_waitMutex;
00296       DUChainPrivate* m_data;
00297   };
00298 public:
00299   DUChainPrivate() : m_chainsMutex(QMutex::Recursive), instance(0), m_cleanupDisabled(false), m_destroyed(false), m_environmentListInfo("Environment Lists"), m_environmentInfo("Environment Information")
00300   {
00301 #if defined(TEST_NO_CLEANUP)
00302     m_cleanupDisabled = true;
00303 #endif
00304 
00305     m_chainsByIndex.set_empty_key(0);
00306     m_chainsByIndex.set_deleted_key(0xffffffff);
00307     duChainPrivateSelf = this;
00308     qRegisterMetaType<DUChainBasePointer>("KDevelop::DUChainBasePointer");
00309     qRegisterMetaType<DUContextPointer>("KDevelop::DUContextPointer");
00310     qRegisterMetaType<TopDUContextPointer>("KDevelop::TopDUContextPointer");
00311     qRegisterMetaType<DeclarationPointer>("KDevelop::DeclarationPointer");
00312     qRegisterMetaType<FunctionDeclarationPointer>("KDevelop::FunctionDeclarationPointer");
00313     qRegisterMetaType<KDevelop::IndexedString>("KDevelop::IndexedString");
00314     qRegisterMetaType<KDevelop::IndexedTopDUContext>("KDevelop::IndexedTopDUContext");
00315     qRegisterMetaType<KDevelop::ReferencedTopDUContext>("KDevelop::ReferencedTopDUContext");
00316 
00317     notifier = new DUChainObserver();
00318     instance = new DUChain();
00319     m_cleanup = new CleanupThread(this);
00320     m_cleanup->start();
00321     
00322     duChainDeleted = false;
00323     
00324     {
00326       QFile f(globalItemRepositoryRegistry().path() + "/parsing_environment_data");
00327       bool opened = f.open(QIODevice::ReadOnly);
00328       ParsingEnvironmentFile::m_staticData = (StaticParsingEnvironmentData*) new char[sizeof(StaticParsingEnvironmentData)];
00329       if(opened) {
00330         kDebug() << "reading parsing-environment static data";
00331         //Read
00332         f.read((char*)ParsingEnvironmentFile::m_staticData, sizeof(StaticParsingEnvironmentData));
00333       }else{
00334         kDebug() << "creating new parsing-environment static data";
00335         //Initialize
00336         new (ParsingEnvironmentFile::m_staticData) StaticParsingEnvironmentData();
00337       }
00338     }
00339   }
00340   ~DUChainPrivate() {
00341     duChainDeleted = true;
00342     m_cleanup->stopThread();
00343     delete m_cleanup;
00344     delete instance;
00345   }
00346 
00347   void clear() {
00348 
00349     if(!m_cleanupDisabled)
00350       doMoreCleanup();
00351     
00352     QMutexLocker l(&m_chainsMutex);
00353 
00354     DUChainWriteLocker writeLock(DUChain::lock());
00355 
00356     for(google::dense_hash_map<uint, TopDUContext*, ItemRepositoryIndexHash>::const_iterator it = m_chainsByIndex.begin(); it != m_chainsByIndex.end(); ++it)
00357       removeDocumentChainFromMemory((*it).second);
00358 
00359     m_indexEnvironmentInformations.clear();
00360     m_fileEnvironmentInformations.clear();
00361 
00362     Q_ASSERT(m_fileEnvironmentInformations.isEmpty());
00363     Q_ASSERT(m_chainsByUrl.isEmpty());
00364     Q_ASSERT(m_chainsByIndex.empty());
00365   }
00366   
00369   void removeDocumentChainFromMemory(TopDUContext* context) {
00370     QMutexLocker l(&m_chainsMutex);
00371 
00372     {
00373       QMutexLocker l(&m_referenceCountsMutex);
00374 
00375       if(m_referenceCounts.contains(context)) {
00376       //This happens during shutdown, since everything is unloaded
00377       kDebug() << "removed a top-context that was reference-counted:" << context->url().str() << context->ownIndex();
00378       m_referenceCounts.remove(context);
00379       }
00380     }
00381 
00382     uint index = context->ownIndex();
00383 
00384   //   kDebug(9505) << "duchain: removing document" << context->url().str();
00385     google::dense_hash_map<uint, TopDUContext*, ItemRepositoryIndexHash>::iterator it = m_chainsByIndex.find(index);
00386     
00387     Q_ASSERT(it != m_chainsByIndex.end());
00388     m_chainsByUrl.remove(context->url(), context);
00389 
00390     if (context->smartRange())
00391       ICore::self()->languageController()->backgroundParser()->removeManagedTopRange(context->smartRange());
00392 
00393     if(!context->isOnDisk())
00394       instance->removeFromEnvironmentManager(context);
00395 
00396     l.unlock();
00397     //DUChain is write-locked, so we can do whatever we want on the top-context, including deleting it
00398     context->deleteSelf();
00399     l.relock();
00400     it = m_chainsByIndex.find(index); //May have been changed due to loading
00401     Q_ASSERT(it != m_chainsByIndex.end());
00402     m_chainsByIndex.erase(it);
00403     
00404     Q_ASSERT(m_chainsByIndex.find(index) == m_chainsByIndex.end());
00405   }
00406 
00409   QMutex m_chainsMutex;
00410 
00411   CleanupThread* m_cleanup;
00412 
00413   DUChain* instance;
00414   DUChainLock lock;
00415   QMultiMap<IndexedString, TopDUContext*> m_chainsByUrl;
00416   google::dense_hash_map<uint, TopDUContext*, ItemRepositoryIndexHash> m_chainsByIndex;
00417 
00418   //Must be locked before accessing m_referenceCounts
00419   QMutex m_referenceCountsMutex;
00420   QHash<TopDUContext*, uint> m_referenceCounts;
00421 
00422   DUChainObserver* notifier;
00423   Definitions m_definitions;
00424   Uses m_uses;
00425   QSet<uint> m_loading;
00426   bool m_cleanupDisabled;
00427 
00429   QSet<ReferencedTopDUContext> m_openDocumentContexts;
00430 
00431   bool m_destroyed;
00432 
00435   void addEnvironmentInformation(ParsingEnvironmentFilePointer info) {
00436     Q_ASSERT(!findInformation(info->indexedTopContext().index()));
00437     Q_ASSERT(m_environmentInfo.findIndex(info->indexedTopContext().index()) == 0);
00438 
00439     QMutexLocker lock(&m_chainsMutex);
00440     m_fileEnvironmentInformations.insert(info->url(), info);
00441     
00442     m_indexEnvironmentInformations.insert(info->indexedTopContext().index(), info);
00443     
00444     Q_ASSERT(info->d_func()->classId);
00445   }
00446 
00449   void removeEnvironmentInformation(ParsingEnvironmentFilePointer info) {
00450 
00451     info->makeDynamic(); //By doing this, we make sure the data is actually being destroyed in the destructor
00452 
00453     m_chainsMutex.lock();
00454     bool removed = m_fileEnvironmentInformations.remove(info->url(), info);
00455     
00456     bool removed2 = m_indexEnvironmentInformations.remove(info->indexedTopContext().index());
00457     
00458     m_chainsMutex.unlock();
00459     
00460     {
00461       //Remove it from the environment information lists if it was there
00462       QMutexLocker lock(m_environmentListInfo.mutex());
00463       uint index = m_environmentListInfo.findIndex(info->url());
00464       
00465       if(index) {
00466         EnvironmentInformationListItem item(*m_environmentListInfo.itemFromIndex(index));
00467         if(item.itemsList().removeOne(info->indexedTopContext().index())) {
00468           m_environmentListInfo.deleteItem(index);
00469           if(!item.itemsList().isEmpty())
00470             m_environmentListInfo.index(EnvironmentInformationListRequest(info->url(), item));
00471         }
00472       }
00473     }
00474     
00475     QMutexLocker lock(m_environmentInfo.mutex());
00476     uint index = m_environmentInfo.findIndex(info->indexedTopContext().index());
00477     if(index) {
00478       m_environmentInfo.deleteItem(index);
00479     }
00480 
00481     Q_ASSERT(index || (removed && removed2));
00482     Q_ASSERT(!findInformation(info->indexedTopContext().index()));
00483   }
00484 
00486   QList<ParsingEnvironmentFilePointer> getEnvironmentInformation(IndexedString url) {
00487     QList<ParsingEnvironmentFilePointer> ret;
00488     uint listIndex = m_environmentListInfo.findIndex(url);
00489 
00490     if(listIndex) {
00491       KDevVarLengthArray<uint> topContextIndices;
00492       
00493       {
00494         //First store all the possible intices into the KDevVarLengthArray, so we can unlock the mutex before processing them.
00495         
00496         QMutexLocker lock(m_environmentListInfo.mutex()); //Lock the mutex to make sure the item isn't changed while it's being iterated
00497         const EnvironmentInformationListItem* item = m_environmentListInfo.itemFromIndex(listIndex);
00498         FOREACH_FUNCTION(uint topContextIndex, item->items)
00499           topContextIndices << topContextIndex;
00500       }
00501       
00502       //Process the indices in a separate step after copying them from the array, so we don't need m_environmentListInfo.mutex locked,
00503       //and can call loadInformation(..) safely, which else might lead to a deadlock.
00504       FOREACH_ARRAY(uint topContextIndex, topContextIndices) {
00505         KSharedPtr< ParsingEnvironmentFile > p = ParsingEnvironmentFilePointer(loadInformation(topContextIndex));
00506         if(p) {
00507          ret << p;
00508         }else{
00509           kDebug() << "Failed to load enviromment-information for" << TopDUContextDynamicData::loadUrl(topContextIndex).str();
00510         }
00511       }
00512     }
00513 
00514     QMutexLocker l(&m_chainsMutex);
00515     
00516     //Add those information that have not been added to the stored lists yet
00517     foreach(ParsingEnvironmentFilePointer file, m_fileEnvironmentInformations.values(url))
00518       if(!ret.contains(file))
00519         ret << file;
00520 
00521     return ret;
00522   }
00523 
00526   void loadChain(uint index, QSet<uint>& loaded) {
00527 
00528     QMutexLocker l(&m_chainsMutex);
00529 
00530     if(m_chainsByIndex.find(index) == m_chainsByIndex.end()) {
00531       if(m_loading.contains(index)) {
00532         //It's probably being loaded by another thread. So wait until the load is ready
00533         while(m_loading.contains(index)) {
00534           l.unlock();
00535           kDebug() << "waiting for another thread to load index" << index;
00536           usleep(50000);
00537           l.relock();
00538         }
00539         loaded.insert(index);
00540         return;
00541       }
00542       m_loading.insert(index);
00543       loaded.insert(index);
00544       
00545       l.unlock();
00546       kDebug() << "loading top-context" << index;
00547       TopDUContext* chain = TopDUContextDynamicData::load(index);
00548       if(chain) {
00549         chain->setParsingEnvironmentFile(loadInformation(chain->ownIndex()));
00550 
00551         if(!chain->usingImportsCache()) {
00552           //Eventually also load all the imported chains, so the import-structure is built
00553           foreach(const DUContext::Import &import, chain->DUContext::importedParentContexts()) {
00554             if(!loaded.contains(import.topContextIndex())) {
00555               loadChain(import.topContextIndex(), loaded);
00556             }
00557           }
00558          }
00559         chain->rebuildDynamicImportStructure();
00560 
00561         chain->setInDuChain(true);
00562         instance->addDocumentChain(chain);
00563       }
00564       
00565       l.relock();
00566       m_loading.remove(index);
00567     }
00568   }
00569 
00570 
00574   void storeAllInformation(bool atomic, DUChainWriteLocker& locker) {
00575 
00576     uint cnt = 0;
00577 
00578     QList<IndexedString> urls;
00579     {
00580       QMutexLocker lock(&m_chainsMutex);
00581       urls += m_fileEnvironmentInformations.keys();
00582     }
00583 
00584     foreach(const IndexedString &url, urls) {
00585       QList<ParsingEnvironmentFilePointer> check;
00586       {
00587         QMutexLocker lock(&m_chainsMutex);
00588         check = m_fileEnvironmentInformations.values(url);
00589       }
00590 
00591       foreach(ParsingEnvironmentFilePointer file, check) {
00592 
00593         EnvironmentInformationRequest req(file.data());
00594         QMutexLocker lock(m_environmentInfo.mutex());
00595         uint index = m_environmentInfo.findIndex(req);
00596 
00597         if(file->d_func()->isDynamic()) {
00598           //This item has been changed, or isn't in the repository yet
00599 
00600           //Eventually remove an old entry
00601           if(index)
00602             m_environmentInfo.deleteItem(index);
00603 
00604           //Add the new entry to the item repository
00605           index = m_environmentInfo.index(req);
00606           Q_ASSERT(index);
00607 
00608           EnvironmentInformationItem* item = const_cast<EnvironmentInformationItem*>(m_environmentInfo.itemFromIndex(index));
00609           DUChainBaseData* theData = (DUChainBaseData*)(((char*)item) + sizeof(EnvironmentInformationItem));
00610           static DUChainBaseData* dataCopy;
00611           dataCopy = theData;
00612 
00613           Q_ASSERT(theData->m_range == file->d_func()->m_range);
00614           Q_ASSERT(theData->m_dynamic == false);
00615           Q_ASSERT(theData->classId == file->d_func()->classId);
00616 
00617           file->setData( theData );
00618 
00619           ++cnt;
00620         }else{
00621           m_environmentInfo.itemFromIndex(index); //Prevent unloading of the data, by accessing the item
00622         }
00623       }
00624 
00627       if(!atomic && (cnt % 100 == 0)) {
00628           //Release the lock on a regular basis
00629           locker.unlock();
00630           locker.lock();
00631       }
00632 
00633       storeInformationList(url);
00634 
00635       //Access the data in the repository, so the bucket isn't unloaded
00636       uint index = m_environmentListInfo.findIndex(EnvironmentInformationListRequest(url));
00637       if(index) {
00638         m_environmentListInfo.itemFromIndex(index);
00639       }else{
00640         QMutexLocker lock(&m_chainsMutex);
00641         kDebug(9505) << "Did not find stored item for" << url.str() << "count:" << m_fileEnvironmentInformations.values(url);
00642       }
00643       if(!atomic) {
00644         locker.unlock();
00645         locker.lock();
00646       }
00647     }
00648   }
00649   
00650   QMutex& cleanupMutex() {
00651     static QMutex mutex(QMutex::Recursive);
00652     return mutex;
00653   }
00654 
00659   void doMoreCleanup(int retries = 0, bool needLockRepository = true) {
00660 
00661     if(m_cleanupDisabled)
00662       return;
00663 
00664     //This mutex makes sure that there's never 2 threads at he same time trying to clean up
00665     QMutexLocker lockCleanupMutex(&cleanupMutex());
00666 
00667     if(m_destroyed || m_cleanupDisabled)
00668       return;
00669     
00670     Q_ASSERT(!instance->lock()->currentThreadHasReadLock() && !instance->lock()->currentThreadHasWriteLock());
00671     DUChainWriteLocker writeLock(instance->lock());
00672     PersistentSymbolTable::self().clearCache();
00673 
00674     //This is used to stop all parsing before starting to do the cleanup. This way less happens during the
00675     //soft cleanups, and we have a good chance that during the "hard" cleanup only few data has to be written.
00676     QList<ILanguage*> lockedParseMutexes;
00677 
00678     QList<QReadWriteLock*> locked;
00679 
00680     if(needLockRepository) {
00681 
00682       if (ICore* core = ICore::self())
00683         if (ILanguageController* lc = core->languageController())
00684           lockedParseMutexes = lc->loadedLanguages();
00685 
00686       writeLock.unlock();
00687 
00688       //Here we wait for all parsing-threads to stop their processing
00689       foreach(ILanguage* language, lockedParseMutexes) {
00690         language->parseLock()->lockForWrite();
00691         locked << language->parseLock();
00692       }
00693 
00694       writeLock.lock();
00695 
00696       globalItemRepositoryRegistry().lockForWriting();
00697       kDebug(9505) << "starting cleanup";
00698     }
00699 
00700     QTime startTime = QTime::currentTime();
00701 
00702     storeAllInformation(!retries, writeLock); //Puts environment-information into a repository
00703 
00704     //We don't need to increase the reference-count, since the cleanup-mutex is locked
00705     QSet<TopDUContext*> workOnContexts;
00706 
00707     {
00708       QMutexLocker l(&m_chainsMutex);
00709       
00710       for(google::dense_hash_map<uint, TopDUContext*, ItemRepositoryIndexHash>::const_iterator it = m_chainsByIndex.begin(); it != m_chainsByIndex.end(); ++it)
00711         workOnContexts << (*it).second;
00712     }
00713 
00714     foreach(TopDUContext* context, workOnContexts) {
00715 
00716       context->m_dynamicData->store();
00717       
00718       if(retries) {
00719         //Eventually give other threads a chance to access the duchain
00720         writeLock.unlock();
00721         //Sleep to give the other threads a realistic chance to get a read-lock in between
00722         usleep(500);
00723         writeLock.lock();
00724       }
00725     }
00726 
00727       //Unload all top-contexts that don't have a reference-count and that are not imported by a referenced one
00728 
00729       QSet<IndexedString> unloadedNames;
00730       bool unloadedOne = true;
00731 
00732       bool unloadAllUnreferenced = !retries;
00733 
00734       //Now unload contexts, but only ones that are not imported by any other currently loaded context
00735       //The complication: Since during the lock-break new references may be added, we must never keep
00736       //the du-chain in an invalid state. Thus we can only unload contexts that are not imported by any
00737       //currently loaded contexts. In case of loops, we have to unload everything at once.
00738       while(unloadedOne) {
00739         unloadedOne = false;
00740         int hadUnloadable = 0;
00741 
00742         unloadContexts:
00743 
00744         foreach(TopDUContext* unload, workOnContexts) {
00745 
00746           bool hasReference = false;
00747 
00748           {
00749             QMutexLocker l(&m_referenceCountsMutex);
00750             //Test if the context is imported by a referenced one
00751             foreach(TopDUContext* context, m_referenceCounts.keys()) {
00752               if(context == unload || context->imports(unload, SimpleCursor())) {
00753                 workOnContexts.remove(unload);
00754                 hasReference = true;
00755               }
00756             }
00757           }
00758 
00759           if(!hasReference)
00760             ++hadUnloadable; //We have found a context that is not referenced
00761           else
00762             continue; //This context is referenced
00763 
00764           bool isImportedByLoaded = !unload->loadedImporters().isEmpty();
00765 
00766           //If we unload a context that is imported by other contexts, we create a bad loaded state
00767           if(isImportedByLoaded && !unloadAllUnreferenced)
00768             continue;
00769 
00770           unloadedNames.insert(unload->url());
00771           //Since we've released the write-lock in between, we've got to call store() again to be sure that none of the data is dynamic
00772           //If nothing has changed, it is only a low-cost call.
00773           unload->m_dynamicData->store();
00774           Q_ASSERT(!unload->d_func()->m_dynamic);
00775           removeDocumentChainFromMemory(unload);
00776           workOnContexts.remove(unload);
00777           unloadedOne = true;
00778 
00779           if(!unloadAllUnreferenced) {
00780             //Eventually give other threads a chance to access the duchain
00781             writeLock.unlock();
00782             //Sleep to give the other threads a realistic chance to get a read-lock in between
00783             usleep(500);
00784             writeLock.lock();
00785           }
00786         }
00787         if(hadUnloadable && !unloadedOne) {
00788           Q_ASSERT(!unloadAllUnreferenced);
00789           //This can happen in case of loops. We have o unload everything at one time.
00790           kDebug(9505) << "found" << hadUnloadable << "unloadable contexts, but could not unload separately. Unloading atomically.";
00791           unloadAllUnreferenced = true;
00792           hadUnloadable = 0; //Reset to 0, so we cannot loop forever
00793           goto unloadContexts;
00794         }
00795       }
00796 
00797 
00798       if(retries == 0) {
00799         QMutexLocker lock(&m_chainsMutex);
00800         //Do this atomically, since we must be sure that _everything_ is already saved
00801         for(QMultiMap<IndexedString, ParsingEnvironmentFilePointer>::iterator it = m_fileEnvironmentInformations.begin(); it != m_fileEnvironmentInformations.end(); ) {
00802           ParsingEnvironmentFile* f = (*it).data();
00803           Q_ASSERT(f->d_func()->classId);
00804           if(f->ref == 1) {
00805             Q_ASSERT(!f->d_func()->isDynamic()); //It cannot be dynamic, since we have stored before
00806             //The ParsingEnvironmentFilePointer is only referenced once. This means that it does not belong to any
00807             //loaded top-context, so just remove it to save some memory and processing time.
00809             it = m_fileEnvironmentInformations.erase(it);
00810           }else{
00811             ++it;
00812           }
00813         }
00814       }
00815 
00816       if(retries)
00817         writeLock.unlock();
00818 
00819       //This must be the last step, due to the on-disk reference counting
00820       globalItemRepositoryRegistry().store(); //Stores all repositories
00821 
00822       {
00823         //Store the static parsing-environment file data
00825         Q_ASSERT(ParsingEnvironmentFile::m_staticData);
00826         QFile f(globalItemRepositoryRegistry().path() + "/parsing_environment_data");
00827         bool opened = f.open(QIODevice::WriteOnly);
00828         Q_ASSERT(opened);
00829         f.write((char*)ParsingEnvironmentFile::m_staticData, sizeof(StaticParsingEnvironmentData));
00830       }
00831       
00832       if(retries) {
00833         doMoreCleanup(retries-1, false);
00834         writeLock.lock();
00835       }
00836 
00837       if(needLockRepository) {
00838         globalItemRepositoryRegistry().unlockForWriting();
00839 
00840         int elapsedSeconds = startTime.secsTo(QTime::currentTime());
00841         kDebug(9505) << "seconds spent doing cleanup: " << elapsedSeconds << "top-contexts still open:" << m_chainsByIndex.size();;
00842       }
00843       if(!retries) {
00844         int elapesedMilliSeconds = startTime.msecsTo(QTime::currentTime());
00845         kDebug(9505) << "milliseconds spent doing cleanup with locked duchain: " << elapesedMilliSeconds;
00846       }
00847 
00848       foreach(QReadWriteLock* lock, locked)
00849         lock->unlock();
00850   }
00851 
00853   ParsingEnvironmentFile* findInformation(uint topContextIndex) {
00854     QMutexLocker lock(&m_chainsMutex);
00855     QHash<uint, ParsingEnvironmentFilePointer>::iterator it = m_indexEnvironmentInformations.find(topContextIndex);
00856     if(it != m_indexEnvironmentInformations.end())
00857       return (*it).data();
00858     return 0;
00859   }
00860 
00864   ParsingEnvironmentFile* loadInformation(uint topContextIndex) {
00865 
00866     ParsingEnvironmentFile* alreadyLoaded = findInformation(topContextIndex);
00867     if(alreadyLoaded)
00868       return alreadyLoaded;
00869 
00870     //Step two: Check if it is on disk, and if is, load it
00871     uint dataIndex = m_environmentInfo.findIndex(EnvironmentInformationRequest(topContextIndex));
00872     if(!dataIndex) {
00873       //No environment-information stored for this top-context
00874       return 0;
00875     }
00876 
00877     const EnvironmentInformationItem& item(*m_environmentInfo.itemFromIndex(dataIndex));
00878 
00879     QMutexLocker lock(&m_chainsMutex);
00880 
00881     //Due to multi-threading, we must do this check after locking the mutex, so we can be sure we don't create the same item twice at the same time
00882     alreadyLoaded = findInformation(topContextIndex);
00883     if(alreadyLoaded)
00884       return alreadyLoaded;
00885     
00886     ParsingEnvironmentFile* ret = dynamic_cast<ParsingEnvironmentFile*>(DUChainItemSystem::self().create( (DUChainBaseData*)(((char*)&item) + sizeof(EnvironmentInformationItem)) ));
00887     if(ret) {
00888       Q_ASSERT(ret->d_func()->classId);
00889       Q_ASSERT(ret->indexedTopContext().index() == topContextIndex);
00890       ParsingEnvironmentFilePointer retPtr(ret);
00891       
00892       m_fileEnvironmentInformations.insert(ret->url(), retPtr);
00893       
00894       Q_ASSERT(!m_indexEnvironmentInformations.contains(ret->indexedTopContext().index()));
00895       m_indexEnvironmentInformations.insert(ret->indexedTopContext().index(), retPtr);
00896     }
00897     return ret;
00898   }
00899   
00900   struct CleanupListVisitor {
00901     QList<uint> checkContexts;
00902     bool operator()(const EnvironmentInformationItem* item) {
00903       checkContexts << item->m_topContext;
00904       return true;
00905     }
00906   };
00907 
00909   void cleanupTopContexts() {
00910     DUChainWriteLocker lock( DUChain::lock() );
00911     kDebug() << "cleaning top-contexts";
00912     CleanupListVisitor visitor;
00913     uint startPos = 0;
00914     m_environmentInfo.visitAllItems(visitor);
00915     
00916     int checkContextsCount = maxFinalCleanupCheckContexts;
00917     int percentageOfContexts = (visitor.checkContexts.size() * 100) / minimumFinalCleanupCheckContextsPercentage;
00918     
00919     if(checkContextsCount < percentageOfContexts)
00920       checkContextsCount = percentageOfContexts;
00921     
00922     if(visitor.checkContexts.size() > (int)checkContextsCount)
00923       startPos = qrand() % (visitor.checkContexts.size() - checkContextsCount);
00924     
00925     int endPos = startPos + maxFinalCleanupCheckContexts;
00926     if(endPos > visitor.checkContexts.size())
00927       endPos = visitor.checkContexts.size();
00928     QSet< uint > check;
00929     for(int a = startPos; a < endPos && check.size() < checkContextsCount; ++a)
00930       if(check.size() < checkContextsCount)
00931         addContextsForRemoval(check, IndexedTopDUContext(visitor.checkContexts[a]));
00932 
00933     foreach(uint topIndex, check) {
00934       IndexedTopDUContext top(topIndex);
00935       if(top.data()) {
00936         kDebug() << "removing top-context for" << top.data()->url().str() << "because it is out of date";
00937         instance->removeDocumentChain(top.data());      
00938       }
00939     }
00940     kDebug() << "check ready";
00941   }
00942 
00943 private:
00944   
00945   void addContextsForRemoval(QSet<uint>& topContexts, IndexedTopDUContext top) {
00946     if(topContexts.contains(top.index()))
00947       return;
00948     
00949     KSharedPtr<ParsingEnvironmentFile> info( instance->environmentFileForDocument(top) );
00951     if(info && info->needsUpdate()) {
00952       //This context will be removed
00953     }else{
00954       return;
00955     }
00956     
00957     topContexts.insert(top.index());
00958     
00959     if(info) {
00960       //Check whether importers need to be removed as well
00961       QList< KSharedPtr<ParsingEnvironmentFile> > importers = info->importers();
00962 
00963       QSet< KSharedPtr<ParsingEnvironmentFile> > checkNext;
00964       
00965       //Do breadth first search, so less imports/importers have to be loaded, and a lower depth is reached
00966       
00967       for(QList< KSharedPtr<ParsingEnvironmentFile> >::iterator it = importers.begin(); it != importers.end(); ++it) {
00968         IndexedTopDUContext c = (*it)->indexedTopContext();
00969         if(!topContexts.contains(c.index())) {
00970           topContexts.insert(c.index()); //Prevent useless recursion
00971           checkNext.insert(*it);
00972         }
00973       }
00974       
00975       for(QSet< KSharedPtr<ParsingEnvironmentFile> >::const_iterator it = checkNext.begin(); it != checkNext.end(); ++it) {
00976         topContexts.remove((*it)->indexedTopContext().index()); //Enable full check again
00977         addContextsForRemoval(topContexts, (*it)->indexedTopContext());
00978       }
00979     }
00980   }
00981 
00982   template<class Entry>
00983   bool listContains(const Entry entry, const Entry* list, uint listSize) {
00984     for(uint a = 0; a < listSize; ++a)
00985       if(list[a] == entry)
00986         return true;
00987     return false;
00988   }
00989 
00991   void storeInformationList(IndexedString url) {
00992 
00993     QMutexLocker lock(m_environmentListInfo.mutex());
00994 
00995     EnvironmentInformationListItem newItem;
00996     newItem.m_file = url;
00997     
00998     QSet<uint> newItems;
00999 
01000     {
01001       QMutexLocker lock(&m_chainsMutex);
01002       QMultiMap<IndexedString, ParsingEnvironmentFilePointer>::iterator start = m_fileEnvironmentInformations.lowerBound(url);
01003       QMultiMap<IndexedString, ParsingEnvironmentFilePointer>::iterator end = m_fileEnvironmentInformations.upperBound(url);
01004       
01005       for(QMultiMap<IndexedString, ParsingEnvironmentFilePointer>::iterator it = start; it != end; ++it) {
01006         uint topContextIndex = (*it)->indexedTopContext().index();
01007         newItems.insert(topContextIndex);
01008         newItem.itemsList().append(topContextIndex);
01009       }
01010     }
01011     
01012     uint index = m_environmentListInfo.findIndex(url);
01013     
01014     if(index) {
01015       //We only handle adding items here, since we can never be sure whether everything is loaded
01016       //Removal is handled directly in removeEnvironmentInformation
01017       
01018       const EnvironmentInformationListItem* item = m_environmentListInfo.itemFromIndex(index);
01019       QSet<uint> oldItems;
01020       FOREACH_FUNCTION(uint topContextIndex, item->items) {
01021         oldItems.insert(topContextIndex);
01022         if(!newItems.contains(topContextIndex)) {
01023           newItems.insert(topContextIndex);
01024           newItem.itemsList().append(topContextIndex);
01025         }
01026       }
01027 
01028       if(oldItems == newItems)
01029         return;
01030 
01032       m_environmentListInfo.deleteItem(index); //Remove the previous item
01033     }
01034 
01035     Q_ASSERT(m_environmentListInfo.findIndex(EnvironmentInformationListRequest(url)) == 0);
01036 
01037     //Insert the new item
01038     m_environmentListInfo.index(EnvironmentInformationListRequest(url, newItem));
01039 
01040     Q_ASSERT(m_environmentListInfo.findIndex(EnvironmentInformationListRequest(url)));
01041   }
01042 
01043   //Loaded environment-informations. Protected by m_chainsMutex
01044   QMultiMap<IndexedString, ParsingEnvironmentFilePointer> m_fileEnvironmentInformations;
01045   QHash<uint, ParsingEnvironmentFilePointer> m_indexEnvironmentInformations;
01046   
01050   ItemRepository<EnvironmentInformationListItem, EnvironmentInformationListRequest> m_environmentListInfo;
01052   ItemRepository<EnvironmentInformationItem, EnvironmentInformationRequest> m_environmentInfo;
01053 };
01054 
01055 K_GLOBAL_STATIC(DUChainPrivate, sdDUChainPrivate)
01056 
01057 
01058 DUChain::DUChain()
01059 {
01060   connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(aboutToQuit()));
01061 
01062   connect(EditorIntegrator::notifier(), SIGNAL(documentAboutToBeDeleted(KTextEditor::Document*)), SLOT(documentAboutToBeDeleted(KTextEditor::Document*)));
01063   connect(EditorIntegrator::notifier(), SIGNAL(documentAboutToBeDeletedFinal(KTextEditor::Document*)), SLOT(documentAboutToBeDeletedFinal(KTextEditor::Document*)));
01064   if(ICore::self()) {
01065     Q_ASSERT(ICore::self()->documentController());
01066     connect(ICore::self()->documentController(), SIGNAL(documentLoadedPrepare(KDevelop::IDocument*)), this, SLOT(documentLoadedPrepare(KDevelop::IDocument*)));
01067     connect(ICore::self()->documentController(), SIGNAL(documentUrlChanged(KDevelop::IDocument*)), this, SLOT(documentRenamed(KDevelop::IDocument*)));
01068     connect(ICore::self()->documentController(), SIGNAL(documentActivated(KDevelop::IDocument*)), this, SLOT(documentActivated(KDevelop::IDocument*)));
01069   }
01070 }
01071 
01072 DUChain::~DUChain()
01073 {
01074   duChainDeleted = true;
01075 }
01076 
01077 DUChain* DUChain::self()
01078 {
01079   return sdDUChainPrivate->instance;
01080 }
01081 
01082 DUChainLock* DUChain::lock()
01083 {
01084   return &sdDUChainPrivate->lock;
01085 }
01086 
01087 QList<TopDUContext*> DUChain::allChains() const
01088 {
01089   QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
01090   QList<TopDUContext*> ret;
01091   for(google::dense_hash_map<uint, TopDUContext*, ItemRepositoryIndexHash>::const_iterator it = sdDUChainPrivate->m_chainsByIndex.begin(); it != sdDUChainPrivate->m_chainsByIndex.end(); ++it)
01092     ret << (*it).second;
01093 
01094   return ret;
01095 }
01096 
01097 void DUChain::updateContextEnvironment( TopDUContext* context, ParsingEnvironmentFile* file ) {
01098 
01099   QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
01100 
01101   removeFromEnvironmentManager( context );
01102 
01103   context->setParsingEnvironmentFile( file );
01104 
01105   addToEnvironmentManager( context );
01106 
01107   branchModified(context);
01108 }
01109 
01110 void DUChain::removeDocumentChain( TopDUContext* context )
01111 {
01112   ENSURE_CHAIN_WRITE_LOCKED;
01113   IndexedTopDUContext indexed(context->indexed());
01114   Q_ASSERT(indexed.data() == context); 
01115   branchRemoved(context);
01116   context->m_dynamicData->deleteOnDisk();
01117   Q_ASSERT(indexed.data() == context);
01118   sdDUChainPrivate->removeDocumentChainFromMemory(context);
01119   Q_ASSERT(!indexed.data());
01120   Q_ASSERT(!environmentFileForDocument(indexed));
01121 }
01122 
01123 void DUChain::addDocumentChain( TopDUContext * chain )
01124 {
01125   QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
01126 
01127 //   kDebug(9505) << "duchain: adding document" << chain->url().str() << " " << chain;
01128   Q_ASSERT(chain);
01129   if (chain->smartRange()) {
01130     Q_ASSERT(!chain->smartRange()->parentRange());
01131     ICore::self()->languageController()->backgroundParser()->addManagedTopRange(KUrl(chain->url().str()), chain->smartRange());
01132   }
01133 
01134   Q_ASSERT(sdDUChainPrivate->m_chainsByIndex.find(chain->ownIndex()) == sdDUChainPrivate->m_chainsByIndex.end());
01135 
01136   sdDUChainPrivate->m_chainsByIndex.insert(std::make_pair(chain->ownIndex(), chain));
01137   sdDUChainPrivate->m_chainsByUrl.insert(chain->url(), chain);
01138 
01139 /*  {
01140     //This is just for debugging, and should be disabled later.
01141     int realChainCount = 0;
01142     int proxyChainCount = 0;
01143     for(QMap<IdentifiedFile, TopDUContext*>::const_iterator it = sdDUChainPrivate->m_chains.begin(); it != sdDUChainPrivate->m_chains.end(); ++it) {
01144       if((*it)->flags() & TopDUContext::ProxyContextFlag)
01145         ++proxyChainCount;
01146       else
01147         ++realChainCount;
01148     }
01149 
01150     kDebug(9505) << "new count of real chains: " << realChainCount << " proxy-chains: " << proxyChainCount;
01151   }*/
01152   chain->setInDuChain(true);
01153   
01154   l.unlock();
01155   
01156   addToEnvironmentManager(chain);
01157 
01158   //contextChanged(0L, DUChainObserver::Addition, DUChainObserver::ChildContexts, chain);
01159 
01160   KTextEditor::Document* doc = EditorIntegrator::documentForUrl(chain->url());
01161   if(doc) {
01162     //Make sure the context stays alive at least as long as the context is open
01163     ReferencedTopDUContext ctx(chain);
01164     sdDUChainPrivate->m_openDocumentContexts.insert(ctx);
01165   }
01166 
01167   branchAdded(chain);
01168 }
01169 
01170 void DUChain::addToEnvironmentManager( TopDUContext * chain ) {
01171 
01172   ParsingEnvironmentFilePointer file = chain->parsingEnvironmentFile();
01173   if( !file )
01174     return; //We don't need to manage
01175 
01176   Q_ASSERT(file->indexedTopContext().index() == chain->ownIndex());
01177 
01178   if(ParsingEnvironmentFile* alreadyHave = sdDUChainPrivate->findInformation(file->indexedTopContext().index()))
01179   {
01182     Q_ASSERT(alreadyHave == file.data());
01183     return;
01184   }
01185 
01186   sdDUChainPrivate->addEnvironmentInformation(file);
01187 }
01188 
01189 void DUChain::removeFromEnvironmentManager( TopDUContext * chain ) {
01190 
01191   ParsingEnvironmentFilePointer file = chain->parsingEnvironmentFile();
01192   if( !file )
01193     return; //We don't need to manage
01194 
01195   sdDUChainPrivate->removeEnvironmentInformation(file);
01196 }
01197 
01198 TopDUContext* DUChain::chainForDocument(const KUrl& document) const {
01199   return chainForDocument(IndexedString(document.pathOrUrl()));
01200 }
01201 
01202 bool DUChain::isInMemory(uint topContextIndex) const {
01203   QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
01204 
01205   google::dense_hash_map<uint, TopDUContext*, ItemRepositoryIndexHash>::const_iterator it = sdDUChainPrivate->m_chainsByIndex.find(topContextIndex);
01206   return it != sdDUChainPrivate->m_chainsByIndex.end();
01207 }
01208 
01209 IndexedString DUChain::urlForIndex(uint index) const {
01210   {
01211     QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
01212     google::dense_hash_map<uint, TopDUContext*, ItemRepositoryIndexHash>::const_iterator it = sdDUChainPrivate->m_chainsByIndex.find(index);
01213     if(it != sdDUChainPrivate->m_chainsByIndex.end())
01214       return (*it).second->url();
01215   }
01216   
01217   return TopDUContextDynamicData::loadUrl(index);
01218 }
01219 
01220 
01221 TopDUContext* DUChain::chainForIndex(uint index) const {
01222 
01223   DUChainPrivate* p = (sdDUChainPrivate.operator->());
01224   if(p->m_destroyed)
01225     return 0;
01226 
01227   //QMutexLocker is not used for performance reasons: This function is called extremely often
01228   p->m_chainsMutex.lock();
01229 
01230   google::dense_hash_map<uint, TopDUContext*, ItemRepositoryIndexHash>::const_iterator it = p->m_chainsByIndex.find(index);
01231   if(it != p->m_chainsByIndex.end()) {
01232     TopDUContext* ret = (*it).second;
01233     p->m_chainsMutex.unlock();
01234     return ret;
01235   } else {
01236     p->m_chainsMutex.unlock();
01237     QSet<uint> loaded;
01238     p->loadChain(index, loaded);
01239     p->m_chainsMutex.lock();
01240 
01241     it = p->m_chainsByIndex.find(index);
01242     if(it != p->m_chainsByIndex.end()) {
01243       TopDUContext* ret = (*it).second;
01244       p->m_chainsMutex.unlock();
01245       return ret;
01246     } else {
01247       p->m_chainsMutex.unlock();
01248       return 0;
01249     }
01250   }
01251 }
01252 
01253 TopDUContext* DUChain::chainForDocument(const IndexedString& document) const
01254 {
01255   if(sdDUChainPrivate->m_destroyed)
01256     return 0;
01257 
01258   QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
01259 /*    {
01260       int count = 0;
01261       QMap<IdentifiedFile, TopDUContext*>::Iterator it = sdDUChainPrivate->m_chains.lowerBound(document);
01262       for( ; it != sdDUChainPrivate->m_chains.end() && it.key().url() == document.url(); ++it )
01263         ++count;
01264       if( count > 1 )
01265         kDebug(9505) << "found " << count << " chains for " << document.url().str();
01266 
01267     }*/
01268 
01269   // Match any parsed version of this document
01270   if(sdDUChainPrivate->m_chainsByUrl.contains(document))
01271     return *sdDUChainPrivate->m_chainsByUrl.find(document);
01272 
01273   //Eventually load an existing chain from disk
01274   l.unlock();
01275   
01276   QList<ParsingEnvironmentFilePointer> list = sdDUChainPrivate->getEnvironmentInformation(document);
01277   
01278   foreach(const ParsingEnvironmentFilePointer &file, list) {
01279     if(isInMemory(file->indexedTopContext().index()))
01280       return file->indexedTopContext().data();
01281   }
01282   if(!list.isEmpty())
01283     return list[0]->topContext(); //Load a top-context if there is none in memory
01284 
01285   return 0;
01286 }
01287 
01288 QList<TopDUContext*> DUChain::chainsForDocument(const KUrl& document) const
01289 {
01290   return chainsForDocument(IndexedString(document));
01291 }
01292 
01293 QList<TopDUContext*> DUChain::chainsForDocument(const IndexedString& document) const
01294 {
01295   QList<TopDUContext*> chains;
01296 
01297   if(sdDUChainPrivate->m_destroyed)
01298     return chains;
01299 
01300   QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
01301 
01302   // Match all parsed versions of this document
01303   for (QMultiMap<IndexedString, TopDUContext*>::Iterator it = sdDUChainPrivate->m_chainsByUrl.lowerBound(document); it != sdDUChainPrivate->m_chainsByUrl.end(); ++it) {
01304     if (it.key() == document)
01305       chains << it.value();
01306     else
01307       break;
01308   }
01309 
01310   return chains;
01311 }
01312 
01313 TopDUContext* DUChain::chainForDocument( const KUrl& document, const ParsingEnvironment* environment, bool onlyProxyContexts, bool noProxyContexts ) const {
01314   return chainForDocument( IndexedString(document), environment, onlyProxyContexts, noProxyContexts );
01315 }
01316 
01317 ParsingEnvironmentFilePointer DUChain::environmentFileForDocument( const IndexedString& document, const ParsingEnvironment* environment, bool onlyProxyContexts, bool noProxyContexts ) const {
01318 
01319   if(sdDUChainPrivate->m_destroyed)
01320     return ParsingEnvironmentFilePointer();
01321   QList< ParsingEnvironmentFilePointer> list = sdDUChainPrivate->getEnvironmentInformation(document);
01322 
01323 //    kDebug() << document.str() << ": matching" << list.size() << (onlyProxyContexts ? "proxy-contexts" : (noProxyContexts ? "content-contexts" : "contexts"));
01324 
01325   QList< ParsingEnvironmentFilePointer>::const_iterator it = list.constBegin();
01326   while(it != list.constEnd()) {
01327     if(*it && (*it)->matchEnvironment(environment) && (!onlyProxyContexts || (*it)->isProxyContext()) && (!noProxyContexts || !(*it)->isProxyContext())) {
01328       return *it;
01329     }
01330     ++it;
01331   }
01332   return ParsingEnvironmentFilePointer();
01333 }
01334 
01335 QList<ParsingEnvironmentFilePointer> DUChain::allEnvironmentFiles(const IndexedString& document) {
01336   return sdDUChainPrivate->getEnvironmentInformation(document);
01337 }
01338 
01339 ParsingEnvironmentFilePointer DUChain::environmentFileForDocument(IndexedTopDUContext topContext) const {
01340    if(topContext.index() == 0)
01341      return ParsingEnvironmentFilePointer();
01342 
01343    return ParsingEnvironmentFilePointer(sdDUChainPrivate->loadInformation(topContext.index()));
01344 }
01345 
01346 TopDUContext* DUChain::chainForDocument( const IndexedString& document, const ParsingEnvironment* environment, bool onlyProxyContexts, bool noProxyContexts ) const {
01347 
01348   if(sdDUChainPrivate->m_destroyed)
01349     return 0;
01350   ParsingEnvironmentFilePointer envFile = environmentFileForDocument(document, environment, onlyProxyContexts, noProxyContexts);
01351   if(envFile) {
01352     return envFile->topContext();
01353   }else{
01354     return 0;
01355   }
01356 }
01357 
01358 DUChainObserver* DUChain::notifier()
01359 {
01360   return sdDUChainPrivate->notifier;
01361 }
01362 
01363 void DUChain::branchAdded(DUContext* context)
01364 {
01365   emit sdDUChainPrivate->notifier->branchAdded(DUContextPointer(context));
01366 }
01367 
01368 void DUChain::branchModified(DUContext* context)
01369 {
01370   emit sdDUChainPrivate->notifier->branchModified(DUContextPointer(context));
01371 }
01372 
01373 void DUChain::branchRemoved(DUContext* context)
01374 {
01375   emit sdDUChainPrivate->notifier->branchRemoved(DUContextPointer(context));
01376 }
01377 
01378 QList<KUrl> DUChain::documents() const
01379 {
01380   QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
01381   
01382   QList<KUrl> ret;
01383   for(google::dense_hash_map<uint, TopDUContext*, ItemRepositoryIndexHash>::const_iterator it = sdDUChainPrivate->m_chainsByIndex.begin(); it != sdDUChainPrivate->m_chainsByIndex.end(); ++it) {
01384     ret << KUrl((*it).second->url().str());
01385   }
01386 
01387   return ret;
01388 }
01389 
01390 /*Q_SCRIPTABLE bool DUChain::updateContext(TopDUContext* topContext, TopDUContext::Features minFeatures, QObject* notifyReady) const
01391 {
01392   if( (topContext->features() & minFeatures) != minFeatures || (topContext->parsingEnvironmentFile() && topContext->parsingEnvironmentFile()->needsUpdate()) ) {
01393     ICore::self()->languageController()->backgroundParser()->addUpdateJob(topContext, minFeatures, notifyReady);
01394     return true;
01395   }else{
01396     //No update needed, or we don't know since there's no ParsingEnvironmentFile attached
01397     return false;
01398   }
01399 }*/
01400 
01401 void DUChain::documentActivated(KDevelop::IDocument* doc)
01402 {
01403   if(sdDUChainPrivate->m_destroyed)
01404     return;
01405   //Check whether the document has an attached environment-manager, and whether that one thinks the document needs to be updated.
01406   //If yes, update it.
01407   DUChainReadLocker lock( DUChain::lock() );
01408   QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
01409   TopDUContext* ctx = DUChainUtils::standardContextForUrl(doc->url());
01410   if(ctx && ctx->parsingEnvironmentFile())
01411     if(ctx->parsingEnvironmentFile()->needsUpdate())
01412       ICore::self()->languageController()->backgroundParser()->addDocument(doc->url());
01413 }
01414 
01415 static void deconvertDUChainInternal(DUContext* context)
01416 {
01417   foreach (Declaration* dec, context->localDeclarations())
01418     dec->clearSmartRange();
01419 
01420   context->clearUseSmartRanges();
01421 
01422   foreach (DUContext* child, context->childContexts())
01423     deconvertDUChainInternal(child);
01424 
01425   context->clearSmartRange();
01426 }
01427 
01428 void DUChain::documentAboutToBeDeletedFinal(KTextEditor::Document* doc)
01429 {
01430   if(sdDUChainPrivate->m_destroyed)
01431     return;
01432   
01433   QList<TopDUContext*> chains = chainsForDocument(doc->url());
01434 
01435   KTextEditor::SmartInterface* smart = dynamic_cast<KTextEditor::SmartInterface*>(doc);
01436   if(!smart)
01437     return;
01438   
01439   foreach (TopDUContext* top, chains) {
01440     
01441     QMutexLocker lockSmart(smart->smartMutex());
01442     
01443     deconvertDUChainInternal(top);
01444   }
01445 }
01446 
01447 void DUChain::documentAboutToBeDeleted(KTextEditor::Document* doc)
01448 {
01449   if(sdDUChainPrivate->m_destroyed)
01450     return;
01451 
01452   foreach(const ReferencedTopDUContext &top, sdDUChainPrivate->m_openDocumentContexts) {
01453     if(top->url().str() == doc->url().pathOrUrl())
01454       sdDUChainPrivate->m_openDocumentContexts.remove(top);
01455   }
01456 }
01457 
01458 void DUChain::documentLoadedPrepare(KDevelop::IDocument* doc)
01459 {
01460   if(sdDUChainPrivate->m_destroyed)
01461     return;
01462   DUChainWriteLocker lock( DUChain::lock() );
01463   QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
01464 
01465   // Convert any duchains to the smart equivalent first
01466   EditorIntegrator editor;
01467   if(doc->textDocument())
01468     editor.insertLoadedDocument(doc->textDocument()); //Make sure the editor-integrator knows the document
01469 
01470   TopDUContext* standardContext = DUChainUtils::standardContextForUrl(doc->url());
01471   QList<TopDUContext*> chains = chainsForDocument(doc->url());
01472 
01473   QList<KDevelop::ILanguage*> languages = ICore::self()->languageController()->languagesForUrl(doc->url());
01474 
01475   if(standardContext) {
01476     Q_ASSERT(chains.contains(standardContext)); //We have just loaded it
01477 
01478     {
01480       SmartConverter sc(&editor);
01481       
01482       if(standardContext->smartRange()) {
01483         Q_ASSERT(standardContext->smartRange()->document() == doc->textDocument());
01484         kWarning() << "Strange: context already has smart-range! Probably another document is already open for it. Deconverting";
01485         sc.deconvertDUChain(standardContext);
01486       }
01487       sc.convertDUChain(standardContext);
01488       Q_ASSERT(standardContext->smartRange());
01489       ICore::self()->languageController()->backgroundParser()->addManagedTopRange(doc->url(), standardContext->smartRange());
01490     }
01491 
01492     sdDUChainPrivate->m_openDocumentContexts.insert(standardContext);
01493 
01494     bool needsUpdate = standardContext->parsingEnvironmentFile() && standardContext->parsingEnvironmentFile()->needsUpdate();
01495     if(!needsUpdate) {
01496 
01497         //Only apply the highlighting if we don't need to update, else we might highlight total crap
01498         //Do instant highlighting only if all imports are loaded, to make sure that we don't block the user-interface too long
01499         //Else the highlighting will be done in the background-thread
01500         //This is not exactly right, as the direct imports don't necessarily equal the real imports used by uses
01501         //but it approximates the correct behavior.
01502         bool allImportsLoaded = true;
01503         foreach(DUContext::Import import, standardContext->importedParentContexts())
01504           if(!import.indexedContext().indexedTopContext().isLoaded())
01505             allImportsLoaded = false;
01506 
01507         if(allImportsLoaded) {
01508           foreach( KDevelop::ILanguage* language, languages)
01509             if(language->languageSupport() && language->languageSupport()->codeHighlighting())
01510               language->languageSupport()->codeHighlighting()->highlightDUChain(standardContext);
01511           kDebug() << "highlighted" << doc->url() << "in foreground";
01512           return;
01513         }
01514     }else{
01515       kDebug() << "not highlighting the duchain because the documents needs an update";
01516     }
01517     
01518     if(needsUpdate || !(standardContext->features() & TopDUContext::AllDeclarationsContextsAndUses)) {
01519       ICore::self()->languageController()->backgroundParser()->addDocument(doc->url(), (TopDUContext::Features)(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::ForceUpdate));
01520       return;
01521     }
01522   }
01523     
01524   //Add for highlighting etc.
01525   ICore::self()->languageController()->backgroundParser()->addDocument(doc->url(), TopDUContext::AllDeclarationsContextsAndUses);
01526 }
01527 
01528 void DUChain::documentRenamed(KDevelop::IDocument* doc)
01529 {
01530   if(sdDUChainPrivate->m_destroyed)
01531     return;
01532   
01533   if(!doc->url().isValid()) {
01535     kWarning() << "Strange, url of renamed document is invalid!";
01536   }else{
01537     ICore::self()->languageController()->backgroundParser()->addDocument(doc->url(), (TopDUContext::Features)(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::ForceUpdate));
01538   }
01539 }
01540 
01541 Uses* DUChain::uses()
01542 {
01543   return &sdDUChainPrivate->m_uses;
01544 }
01545 
01546 Definitions* DUChain::definitions()
01547 {
01548   return &sdDUChainPrivate->m_definitions;
01549 }
01550 
01551 void DUChain::aboutToQuit()
01552 {
01553   QMutexLocker lock(&sdDUChainPrivate->cleanupMutex());
01554 
01555   {
01556     //Acquire write-lock of the repository, so when kdevelop crashes in that process, the repository is discarded
01557     //Crashes here may happen in an inconsistent state, thus this makes sense, to protect the user from more crashes
01558     globalItemRepositoryRegistry().lockForWriting();
01559     sdDUChainPrivate->cleanupTopContexts();
01560     globalItemRepositoryRegistry().unlockForWriting();
01561   }
01562 
01563   sdDUChainPrivate->doMoreCleanup(); //Must be done _before_ finalCleanup, else we may be deleting yet needed data
01564 
01565   {
01566     //Acquire write-lock of the repository, so when kdevelop crashes in that process, the repository is discarded
01567     //Crashes here may happen in an inconsistent state, thus this makes sense, to protect the user from more crashes
01568     globalItemRepositoryRegistry().lockForWriting();
01569     finalCleanup();
01570     globalItemRepositoryRegistry().unlockForWriting();
01571   }
01572   
01573   sdDUChainPrivate->doMoreCleanup();
01574   sdDUChainPrivate->m_openDocumentContexts.clear();
01575   sdDUChainPrivate->m_destroyed = true;
01576   sdDUChainPrivate->clear();
01577   
01578   globalItemRepositoryRegistry().shutdown();
01579 }
01580 
01581 uint DUChain::newTopContextIndex() {
01582   static QAtomicInt& currentId( globalItemRepositoryRegistry().getCustomCounter("Top-Context Counter", 1) );
01583   return currentId.fetchAndAddRelaxed(1);
01584 }
01585 
01586 void DUChain::refCountUp(TopDUContext* top) {
01587   QMutexLocker l(&sdDUChainPrivate->m_referenceCountsMutex);
01588   if(!sdDUChainPrivate->m_referenceCounts.contains(top))
01589     sdDUChainPrivate->m_referenceCounts.insert(top, 1);
01590   else
01591     ++sdDUChainPrivate->m_referenceCounts[top];
01592 }
01593 
01594 bool DUChain::deleted() {
01595   return duChainDeleted;
01596 }
01597 
01598 void DUChain::refCountDown(TopDUContext* top) {
01599   QMutexLocker l(&sdDUChainPrivate->m_referenceCountsMutex);
01600   if(!sdDUChainPrivate->m_referenceCounts.contains(top)) {
01601     //kWarning() << "tried to decrease reference-count for" << top->url().str() << "but this top-context is not referenced";
01602     return;
01603   }
01604   --sdDUChainPrivate->m_referenceCounts[top];
01605   if(!sdDUChainPrivate->m_referenceCounts[top])
01606     sdDUChainPrivate->m_referenceCounts.remove(top);
01607 }
01608 
01609 void DUChain::emitDeclarationSelected(DeclarationPointer decl) {
01610   emit declarationSelected(decl);
01611 }
01612 
01613 KDevelop::ReferencedTopDUContext DUChain::waitForUpdate(const KDevelop::IndexedString& document, KDevelop::TopDUContext::Features minFeatures, bool wantProxyContext) {
01614   Q_ASSERT(!lock()->currentThreadHasReadLock() && !lock()->currentThreadHasWriteLock());
01615 
01616   WaitForUpdate waiter;
01617   
01618   waiter.m_dataMutex.lock();
01619   
01620   {
01621     DUChainReadLocker readLock(DUChain::lock());
01622   
01623     updateContextForUrl(document, minFeatures, &waiter);
01624   }
01625   
01626 //   waiter.m_waitMutex.lock();
01627 //   waiter.m_dataMutex.unlock();
01628   while(!waiter.m_ready) {
01631     QApplication::processEvents();
01632     usleep(10000);
01633 //     waiter.m_wait.wait(&waiter.m_waitMutex, 10);
01634   }
01635 
01636   if(!wantProxyContext) {
01637     DUChainReadLocker readLock(DUChain::lock());
01638     if(waiter.m_topContext && waiter.m_topContext->parsingEnvironmentFile() && waiter.m_topContext->parsingEnvironmentFile()->isProxyContext() && !waiter.m_topContext->importedParentContexts().isEmpty())
01639       return waiter.m_topContext->importedParentContexts()[0].context(0)->topContext();
01640   }
01641 
01642   return waiter.m_topContext;
01643 }
01644 
01645 void DUChain::updateContextForUrl(const IndexedString& document, TopDUContext::Features minFeatures, QObject* notifyReady) const {
01646   DUChainReadLocker lock( DUChain::lock() );
01647   TopDUContext* standardContext = DUChainUtils::standardContextForUrl(document.toUrl());
01648   if(standardContext && standardContext->parsingEnvironmentFile() && !standardContext->parsingEnvironmentFile()->needsUpdate() && standardContext->parsingEnvironmentFile()->featuresSatisfied(minFeatures)) {
01649     lock.unlock();
01650     if(notifyReady)
01651     QMetaObject::invokeMethod(notifyReady, "updateReady", Qt::DirectConnection, Q_ARG(KDevelop::IndexedString, document), Q_ARG(KDevelop::ReferencedTopDUContext, ReferencedTopDUContext(standardContext)));
01652   }else{
01654     ICore::self()->languageController()->backgroundParser()->addDocument(document.toUrl(), minFeatures, 1, notifyReady);
01655   }
01656 }
01657 
01658 void DUChain::disablePersistentStorage() {
01659   sdDUChainPrivate->m_cleanupDisabled = true;
01660 }
01661 
01662 void DUChain::storeToDisk() {
01663   bool wasDisabled = sdDUChainPrivate->m_cleanupDisabled;
01664   sdDUChainPrivate->m_cleanupDisabled = false;
01665   
01666   sdDUChainPrivate->doMoreCleanup();
01667   
01668   sdDUChainPrivate->m_cleanupDisabled = wasDisabled;
01669 }
01670 
01671 void DUChain::finalCleanup() {
01672   DUChainWriteLocker writeLock(DUChain::lock());
01673   kDebug() << "doing final cleanup";
01674   
01675   int cleaned = 0;
01676   while((cleaned = globalItemRepositoryRegistry().finalCleanup())) {
01677     kDebug() << "cleaned" << cleaned << "B";
01678     if(cleaned < 1000) {
01679       kDebug() << "cleaned enough";
01680       break;
01681     }
01682   }
01683   kDebug() << "final cleanup ready";
01684 }
01685 
01686 bool DUChain::compareToDisk() {
01687   
01688   DUChainWriteLocker writeLock(DUChain::lock());
01689   
01691   return true;
01692 }
01693 
01694 }
01695 
01696 #include "duchain.moc"
01697 
01698 // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on

language/duchain

Skip menu "language/duchain"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

KDevelop Platform Libraries

Skip menu "KDevelop Platform Libraries"
  • interfaces
  • language
  •   codegen
  •   duchain
  •   editor
  • outputview
  • project
  • shell
  • sublime
  • util
  • vcs
Generated for KDevelop Platform Libraries by doxygen 1.5.9-20090814
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal