/*************************************************************************** * Copyright (C) 2002 by Roberto Raggi * * roberto@kdevelop.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "backgroundparser.h" #include "cppsupportpart.h" #include "cppsupport_events.h" #include "codeinformationrepository.h" #include "cppcodecompletion.h" #include "ast_utils.h" #include "kdevdeepcopy.h" #include "kdevdriver.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class BackgroundKDevDriver : public KDevDriver { public: BackgroundKDevDriver( CppSupportPart* cppSupport, BackgroundParser* bp ) : KDevDriver( cppSupport, false ), m_backgroundParser(bp) { } virtual void fileParsed( ParsedFile& fileName ); virtual void addDependence( const TQString& fileName, const Dependence& dep ); private: BackgroundParser* m_backgroundParser; }; class KDevSourceProvider: public SourceProvider { public: //Deadlock is a mutex that is locked when KDevSourceProvider::contents(..) is used, and that should be unlocked before TQApplication is locked(that way a deadlock where the thread that holds the TQApplication-mutex and tries to lock the given mutex, while the thread that calls contents(..) and holds the given mutex and tries to lock the TQApplication-mutex, cannot happen) KDevSourceProvider( CppSupportPart* cppSupport, TQMutex& deadlock ) : m_cppSupport( cppSupport ), m_readFromDisk( false ), m_deadlock(deadlock) {} void setReadFromDisk( bool b ) { m_readFromDisk = b; } bool readFromDisk() const { return m_readFromDisk; } virtual TQString contents( const TQString& fileName ) { TQString contents = TQString(); if ( !m_readFromDisk ) { m_deadlock.unlock(); // GET LOCK kapp->lock (); //kdDebug(9007) << "-------> kapp locked" << endl; TQPtrList parts( *m_cppSupport->partController() ->parts() ); TQPtrListIterator it( parts ); while ( it.current() ) { KTextEditor::Document * doc = dynamic_cast( it.current() ); ++it; KTextEditor::EditInterface* editIface = dynamic_cast( doc ); if ( !doc || !editIface || doc->url().path() != fileName ) continue; contents = TQString( editIface->text().ascii() ); // deep copy //kdDebug(9007) << "-------> kapp unlocked" << endl; break; } // RELEASE LOCK kapp->unlock(); m_deadlock.lock(); //kdDebug(9007) << "-------> kapp unlocked" << endl; } if( m_readFromDisk || contents == TQString() ) { TQFile f( fileName ); if ( f.open( IO_ReadOnly ) ) { TQTextStream stream( &f ); contents = stream.read(); f.close(); } } return contents; } virtual bool isModified( const TQString& fileName ) { bool ret = false; m_deadlock.unlock(); kapp->lock (); KParts::ReadOnlyPart *part = m_cppSupport->partController()->partForURL( KURL(fileName) ); KTextEditor::Document * doc = dynamic_cast( part ); if ( doc ) ret = doc->isModified(); kapp->unlock(); m_deadlock.lock(); return ret; } private: CppSupportPart* m_cppSupport; bool m_readFromDisk; TQMutex& m_deadlock; private: KDevSourceProvider( const KDevSourceProvider& source ); void operator = ( const KDevSourceProvider& source ); }; typedef std::string SafeString; class SynchronizedFileList { typedef std::list< TQPair > ListType; public: SynchronizedFileList() {} bool isEmpty() const { TQMutexLocker locker( &m_mutex ); return m_fileList.empty(); } uint count() const { TQMutexLocker locker( &m_mutex ); return m_fileList.size(); } TQPair front() const { TQMutexLocker locker( &m_mutex ); return m_fileList.front(); } void clear() { TQMutexLocker locker( &m_mutex ); m_fileList.clear(); } void push_front( const TQString& fileName, bool readFromDisk = false ) { SafeString s( fileName.ascii() ); TQMutexLocker locker( &m_mutex ); m_fileList.push_front( tqMakePair( s, readFromDisk ) ); } void push_back( const TQString& fileName, bool readFromDisk = false ) { SafeString s( fileName.ascii() ); TQMutexLocker locker( &m_mutex ); m_fileList.push_back( tqMakePair( s, readFromDisk ) ); } void pop_front() { TQMutexLocker locker( &m_mutex ); m_fileList.pop_front(); } int count( const TQString& fileName ) const { int c = 0; TQMutexLocker locker( &m_mutex ); ListType::const_iterator it = m_fileList.begin(); while ( it != m_fileList.end() ) { if ( ( *it ).first.compare( fileName.ascii() ) == 0 ) ++c; ++it; } return c; } TQPair takeFront() { TQMutexLocker locker( &m_mutex ); TQPair ret = m_fileList.front(); m_fileList.pop_front(); return ret; } bool contains( const TQString& fileName ) const { TQMutexLocker locker( &m_mutex ); ListType::const_iterator it = m_fileList.begin(); while ( it != m_fileList.end() ) { if ( ( *it ).first.compare( fileName.ascii() ) == 0 ) return true; ++it; } return false; } void remove( const TQString& fileName ) { TQMutexLocker locker( &m_mutex ); ListType::iterator it = m_fileList.begin(); while ( it != m_fileList.end() ) { if ( ( *it ).first.compare(fileName.ascii() ) == 0 ) m_fileList.erase( it++ ); else ++it; } } private: mutable TQMutex m_mutex; ListType m_fileList; }; BackgroundParser::BackgroundParser( CppSupportPart* part, TQWaitCondition* consumed ) : m_consumed( consumed ), m_cppSupport( part ), m_close( false ), m_saveMemory( false ) { m_fileList = new SynchronizedFileList(); m_driver = new BackgroundKDevDriver( m_cppSupport, this ); m_driver->setSourceProvider( new KDevSourceProvider( m_cppSupport, m_mutex ) ); TQString conf_file_name = m_cppSupport->specialHeaderName(); m_mutex.lock(); if ( TQFile::exists( conf_file_name ) ) m_driver->parseFile( conf_file_name, true, true, true ); m_mutex.unlock(); //disabled for now m_driver->setResolveDependencesEnabled( true ); } BackgroundParser::~BackgroundParser() { removeAllFiles(); delete( m_driver ); m_driver = 0; delete m_fileList; m_fileList = 0; } void BackgroundParser::addFile( const TQString& fileName, bool readFromDisk ) { TQString fn = deepCopy( fileName ); //bool added = false; /*if ( !m_fileList->contains( fn ) ) { m_fileList->push_back( fn, readFromDisk ); added = true; }*/ m_fileList->push_back( fn, readFromDisk ); //if ( added ) m_canParse.wakeAll(); } void BackgroundParser::addFileFront( const TQString& fileName, bool readFromDisk ) { TQString fn = deepCopy( fileName ); bool added = false; /*if ( m_fileList->contains( fn ) ) m_fileList->remove( fn );*/ m_fileList->push_front( fn, readFromDisk ); added = true; if ( added ) m_canParse.wakeAll(); } void BackgroundParser::removeAllFiles() { kdDebug( 9007 ) << "BackgroundParser::removeAllFiles()" << endl; TQMutexLocker locker( &m_mutex ); TQMap::Iterator it = m_unitDict.begin(); while ( it != m_unitDict.end() ) { Unit * unit = it.data(); ++it; delete( unit ); unit = 0; } m_unitDict.clear(); m_driver->reset(); m_fileList->clear(); m_isEmpty.wakeAll(); } void BackgroundParser::removeFile( const TQString& fileName ) { TQMutexLocker locker( &m_mutex ); Unit* unit = findUnit( fileName ); if ( unit ) { m_driver->remove ( fileName ); m_unitDict.remove( fileName ); delete( unit ); unit = 0; } if ( m_fileList->isEmpty() ) m_isEmpty.wakeAll(); } void BackgroundKDevDriver::addDependence( const TQString& fileName, const Dependence& dep ) { //give waiting threads a chance to perform their actions m_backgroundParser->m_mutex.unlock(); m_backgroundParser->m_mutex.lock(); KDevDriver::addDependence( fileName, dep ); } void BackgroundKDevDriver::fileParsed( ParsedFile& fileName ) { m_backgroundParser->fileParsed( fileName ); } void BackgroundParser::parseFile( const TQString& fileName, bool readFromDisk, bool lock ) { if( lock ) m_mutex.lock(); m_readFromDisk = readFromDisk; static_cast( m_driver->sourceProvider() ) ->setReadFromDisk( readFromDisk ); m_driver->remove( fileName ); m_driver->parseFile( fileName , false, true ); if( !m_driver->isResolveDependencesEnabled() ) m_driver->removeAllMacrosInFile( fileName ); // romove all macros defined by this // translation unit. if ( lock ) m_mutex.unlock(); } TQValueList cloneProblemList( const TQValueList& list ) { TQValueList ret; for( TQValueList::const_iterator it = list.begin(); it != list.end(); ++it ) { ret << Problem( *it, true ); } return ret; } void BackgroundParser::fileParsed( ParsedFile& file ) { ParsedFilePointer translationUnitUnsafe = m_driver->takeTranslationUnit( file.fileName() ); //now file and translationUnitUnsafe are the same ParsedFilePointer translationUnit; //Since the lexer-cache keeps many TQStrings like macro-names used in the background, everything must be copied here. The safest solution is just //serializing and deserializing the whole thing(the serialization does not respect the AST, but that can be copied later because that's safe) TQMemArray data; { TQDataStream stream( TQByteArray(data), IO_WriteOnly ); translationUnitUnsafe->write( stream ); } { TQDataStream stream( TQByteArray(data), IO_ReadOnly ); translationUnit = new ParsedFile( stream ); } translationUnit->setTranslationUnit( translationUnitUnsafe->operator TranslationUnitAST *() ); //Copy the AST, doing that is thread-safe translationUnitUnsafe->setTranslationUnit( 0 ); //Move the AST completely out of this thread's scope. Else it might crash on dual-core machines file.setTranslationUnit(0); //just to be sure, set to zero on both Unit* unit = new Unit; unit->fileName = file.fileName(); unit->translationUnit = translationUnit; unit->problems = cloneProblemList( m_driver->problems( file.fileName() ) ); static_cast( m_driver->sourceProvider() ) ->setReadFromDisk( false ); if ( m_unitDict.find( file.fileName() ) != m_unitDict.end() ) { Unit * u = m_unitDict[ file.fileName() ]; m_unitDict.remove( file.fileName() ); delete( u ); u = 0; } m_unitDict.insert( file.fileName(), unit ); TDEApplication::postEvent( m_cppSupport, new FileParsedEvent( file.fileName(), unit->problems, m_readFromDisk ) ); m_currentFile = TQString(); if ( m_fileList->isEmpty() ) m_isEmpty.wakeAll(); } Unit* BackgroundParser::findUnit( const TQString& fileName ) { TQMap::Iterator it = m_unitDict.find( fileName ); return it != m_unitDict.end() ? *it : 0; } bool BackgroundParser::hasTranslationUnit( const TQString& fileName ) { TQMap::Iterator it = m_unitDict.find( fileName ); return it != m_unitDict.end(); } ParsedFilePointer BackgroundParser::translationUnit( const TQString& fileName ) { Unit * u = findUnit( fileName ); if ( u == 0 ) { return 0; /*m_fileList->remove ( fileName ); u = parseFile( fileName, false );*/ } return u->translationUnit; } TQValueList BackgroundParser::problems( const TQString& fileName, bool readFromDisk, bool forceParse ) { Q_UNUSED(readFromDisk); Unit * u = findUnit( fileName ); if ( u == 0 || forceParse ) { /* m_fileList->remove ( fileName ); u = parseFile( fileName, readFromDisk ); */ } return u ? u->problems : TQValueList(); } void BackgroundParser::close() { { TQMutexLocker locker( &m_mutex ); m_close = true; m_canParse.wakeAll(); } kapp->unlock(); while ( running() ) sleep( 1 ); kapp->lock(); } bool BackgroundParser::filesInQueue() { TQMutexLocker locker( &m_mutex ); return m_fileList->count() || !m_currentFile.isEmpty(); } int BackgroundParser::countInQueue( const TQString& file ) const { return m_fileList->count( file ); } void BackgroundParser::updateParserConfiguration() { TQMutexLocker locker( &m_mutex ); m_driver->setup(); TQString conf_file_name = m_cppSupport->specialHeaderName(); m_driver->removeAllMacrosInFile( conf_file_name ); m_driver->parseFile( conf_file_name, true, true, true ); } void BackgroundParser::run() { // (void) m_cppSupport->codeCompletion()->repository()->getEntriesInScope( TQStringList(), false ); while ( !m_close ) { while ( m_fileList->isEmpty() ) { if( m_saveMemory ) { m_saveMemory = false; m_driver->lexerCache()->saveMemory(); } m_canParse.wait(); if ( m_close ) break; } if ( m_close ) break; TQPair entry = m_fileList->takeFront(); TQString fileName = entry.first.c_str(); bool readFromDisk = entry.second; m_currentFile = deepCopy(fileName); ( void ) parseFile( fileName, readFromDisk, true ); m_currentFile = TQString(); } kdDebug( 9007 ) << "!!!!!!!!!!!!!!!!!! BG PARSER DESTROYED !!!!!!!!!!!!" << endl; // adymo: commented to fix #88091 // TQThread::exit(); } void BackgroundParser::saveMemory() { m_saveMemory = true; //Delay the operation m_canParse.wakeAll(); } //kate: indent-mode csands; tab-width 4; space-indent off;