Warning, file /office/kile/src/parser/parserthread.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /************************************************************************** 0002 * Copyright (C) 2011-2012 by Michel Ludwig (michel.ludwig@kdemail.net) * 0003 ***************************************************************************/ 0004 0005 /*************************************************************************** 0006 * * 0007 * This program is free software; you can redistribute it and/or modify * 0008 * it under the terms of the GNU General Public License as published by * 0009 * the Free Software Foundation; either version 2 of the License, or * 0010 * (at your option) any later version. * 0011 * * 0012 ***************************************************************************/ 0013 0014 #include "parserthread.h" 0015 0016 #include "documentinfo.h" 0017 #include "kiledocmanager.h" 0018 #include "kileinfo.h" 0019 #include "bibtexparser.h" 0020 #include "latexparser.h" 0021 #include "latexoutputparser.h" 0022 0023 namespace KileParser { 0024 0025 ParserThread::ParserThread(KileInfo *info, QObject *parent) : 0026 QThread(parent), 0027 m_ki(info), 0028 m_keepParserThreadAlive(true) 0029 { 0030 } 0031 0032 ParserThread::~ParserThread() 0033 { 0034 qCDebug(LOG_KILE_PARSER) << "destroying parser thread" << this; 0035 stopParsing(); 0036 // wait for the thread to finish before it is deleted at 0037 // the end of this destructor 0038 qCDebug(LOG_KILE_PARSER) << "waiting for parser thread to finish..."; 0039 wait(); 0040 // and delete remaining queue items (no mutex is required 0041 // as the thread's execution has stopped) 0042 qDeleteAll(m_parserQueue); 0043 } 0044 0045 void ParserThread::addParserInput(ParserInput *input) 0046 { 0047 qCDebug(LOG_KILE_PARSER) << input; 0048 qCDebug(LOG_KILE_PARSER) << "trying to obtain m_parserMutex"; 0049 0050 m_parserMutex.lock(); 0051 // first, check whether the document is queued already 0052 QQueue<ParserInput*>::iterator it = m_parserQueue.begin(); 0053 for(; it != m_parserQueue.end(); ++it) { 0054 if((*it)->url == input->url) { 0055 break; 0056 } 0057 } 0058 0059 if(it != m_parserQueue.end()) { 0060 qCDebug(LOG_KILE_PARSER) << "document in queue already"; 0061 *it = input; 0062 } 0063 else { 0064 if(m_currentlyParsedUrl == input->url) { 0065 qCDebug(LOG_KILE_PARSER) << "re-parsing document"; 0066 // stop the parsing of the document 0067 m_keepParsingDocument = false; 0068 // and add it as first element to the queue 0069 m_parserQueue.push_front(input); 0070 } 0071 else { 0072 qCDebug(LOG_KILE_PARSER) << "adding to the end"; 0073 m_parserQueue.push_back(input); 0074 } 0075 } 0076 m_parserMutex.unlock(); 0077 0078 // finally, wake up threads waiting for the queue to be filled 0079 m_queueEmptyWaitCondition.wakeAll(); 0080 } 0081 0082 void ParserThread::removeParserInput(const QUrl &url) 0083 { 0084 qCDebug(LOG_KILE_PARSER) << url; 0085 m_parserMutex.lock(); 0086 // first, if the document is currently parsed, we stop the parsing 0087 if(m_currentlyParsedUrl == url) { 0088 qCDebug(LOG_KILE_PARSER) << "document currently being parsed"; 0089 m_keepParsingDocument = false; 0090 } 0091 // nevertheless, we remove all traces of the document from the queue 0092 for(QQueue<ParserInput*>::iterator it = m_parserQueue.begin(); it != m_parserQueue.end();) { 0093 ParserInput *input = *it; 0094 if(input->url == url) { 0095 qCDebug(LOG_KILE_PARSER) << "found it"; 0096 it = m_parserQueue.erase(it); 0097 delete input; 0098 } 0099 else { 0100 ++it; 0101 } 0102 } 0103 m_parserMutex.unlock(); 0104 } 0105 0106 void ParserThread::stopParsing() 0107 { 0108 qCDebug(LOG_KILE_PARSER); 0109 m_parserMutex.lock(); 0110 0111 m_keepParserThreadAlive = false; 0112 m_keepParsingDocument = false; 0113 m_parserMutex.unlock(); 0114 // wake all the threads that are still waiting for the queue to fill up 0115 m_queueEmptyWaitCondition.wakeAll(); 0116 } 0117 0118 bool ParserThread::shouldContinueDocumentParsing() 0119 { 0120 QMutexLocker locker(&m_parserMutex); 0121 return m_keepParsingDocument; 0122 } 0123 0124 bool ParserThread::isParsingComplete() 0125 { 0126 QMutexLocker locker(&m_parserMutex); 0127 // as the parser queue might be empty but a document is still being parsed, 0128 // we additionally have to check whether 'm_currentlyParsedUrl' is empty or not 0129 return m_parserQueue.isEmpty() && m_currentlyParsedUrl.isEmpty(); 0130 } 0131 0132 // the document that is currently parsed is always the head of the queue 0133 void ParserThread::run() 0134 { 0135 ParserInput* currentParsedItem; 0136 qCDebug(LOG_KILE_PARSER) << "starting up..."; 0137 while(true) { 0138 // first, try to extract the head of the queue 0139 m_parserMutex.lock(); 0140 // clear the variable currently parsed url; might be necessary from the previous iteration 0141 m_currentlyParsedUrl = QUrl(); 0142 // check if we should still be running before going to sleep 0143 if(!m_keepParserThreadAlive) { 0144 m_parserMutex.unlock(); 0145 // remaining queue elements are deleted in the destructor 0146 return; 0147 } 0148 // but if there are no items to be parsed, we go to sleep. 0149 // However, we have to be careful and use a 'while' loop here 0150 // as it can happen that an item is added to the queue but this 0151 // thread is woken up only after it has been removed again. 0152 while(m_parserQueue.size() == 0 && m_keepParserThreadAlive) { 0153 qCDebug(LOG_KILE_PARSER) << "going to sleep..."; 0154 emit(parsingQueueEmpty()); 0155 m_queueEmptyWaitCondition.wait(&m_parserMutex); 0156 qCDebug(LOG_KILE_PARSER) << "woken up..."; 0157 } 0158 // threads are woken up when an object of this class is destroyed; in 0159 // that case the queue might still be empty 0160 if(!m_keepParserThreadAlive) { 0161 m_parserMutex.unlock(); 0162 // remaining queue elements are deleted in the destructor 0163 return; 0164 } 0165 Q_ASSERT(m_parserQueue.size() > 0); 0166 qCDebug(LOG_KILE_PARSER) << "queue length" << m_parserQueue.length(); 0167 // now, extract the head 0168 currentParsedItem = m_parserQueue.dequeue(); 0169 0170 m_keepParsingDocument = true; 0171 m_currentlyParsedUrl = currentParsedItem->url; 0172 emit(parsingStarted()); 0173 m_parserMutex.unlock(); 0174 0175 Parser *parser = createParser(currentParsedItem); 0176 0177 ParserOutput *parserOutput = Q_NULLPTR; 0178 if(parser) { 0179 parserOutput = parser->parse(); 0180 } 0181 0182 delete currentParsedItem; 0183 delete parser; 0184 0185 // we also emit when 'parserOutput == Q_NULLPTR' as this will be used to indicate 0186 // that some error has occurred; 0187 // as this call will be blocking, one has to make sure that no mutex is held 0188 emit(parsingComplete(m_currentlyParsedUrl, parserOutput)); 0189 } 0190 qCDebug(LOG_KILE_PARSER) << "leaving..."; 0191 // remaining queue elements are deleted in the destructor 0192 } 0193 0194 DocumentParserThread::DocumentParserThread(KileInfo *info, QObject *parent) 0195 : ParserThread(info, parent) 0196 { 0197 } 0198 0199 DocumentParserThread::~DocumentParserThread() 0200 { 0201 } 0202 0203 Parser* DocumentParserThread::createParser(ParserInput *input) 0204 { 0205 if(dynamic_cast<LaTeXParserInput*>(input)) { 0206 return new LaTeXParser(this, dynamic_cast<LaTeXParserInput*>(input)); 0207 } 0208 else if(dynamic_cast<BibTeXParserInput*>(input)) { 0209 return new BibTeXParser(this, dynamic_cast<BibTeXParserInput*>(input)); 0210 } 0211 0212 return Q_NULLPTR; 0213 } 0214 0215 void DocumentParserThread::addDocument(KileDocument::TextInfo *textInfo) 0216 { 0217 qCDebug(LOG_KILE_PARSER) << textInfo; 0218 const QUrl url = m_ki->docManager()->urlFor(textInfo); 0219 if(url.isEmpty()) { // if the url is empty (which can happen with new documents), 0220 return; // we can't do anything as not even the results of the parsing can be displayed 0221 } 0222 0223 ParserInput* newItem = Q_NULLPTR; 0224 if(dynamic_cast<KileDocument::BibInfo*>(textInfo)) { 0225 newItem = new BibTeXParserInput(url, textInfo->documentContents()); 0226 } 0227 else { 0228 newItem = new LaTeXParserInput(url, textInfo->documentContents(), 0229 m_ki->extensions(), 0230 textInfo->dictStructLevel(), 0231 KileConfig::svShowSectioningLabels(), 0232 KileConfig::svShowTodo()); 0233 } 0234 addParserInput(newItem); 0235 0236 // It is not very useful to watch for the destruction of 'textInfo' here and stop the parsing 0237 // for 'textInfo' whenever that happens as at that moment it probably won't have a document 0238 // anymore nor would it still be associated with a project item. 0239 // It is better to call 'removeDocument' from the point when 'textInfo' is going to be deleted! 0240 } 0241 0242 void DocumentParserThread::removeDocument(KileDocument::TextInfo *textInfo) 0243 { 0244 qCDebug(LOG_KILE_PARSER); 0245 KTextEditor::Document *document = textInfo->getDoc(); 0246 if(!document) { 0247 return; 0248 } 0249 removeParserInput(document->url()); 0250 } 0251 0252 void DocumentParserThread::removeDocument(const QUrl &url) 0253 { 0254 removeParserInput(url); 0255 } 0256 0257 OutputParserThread::OutputParserThread(KileInfo *info, QObject *parent) 0258 : ParserThread(info, parent) 0259 { 0260 } 0261 0262 OutputParserThread::~OutputParserThread() 0263 { 0264 } 0265 0266 Parser* OutputParserThread::createParser(ParserInput *input) 0267 { 0268 if(dynamic_cast<LaTeXOutputParserInput*>(input)) { 0269 return new LaTeXOutputParser(this, dynamic_cast<LaTeXOutputParserInput*>(input)); 0270 } 0271 return Q_NULLPTR; 0272 } 0273 0274 void OutputParserThread::addLaTeXLogFile(const QString& logFile, const QString& sourceFile, 0275 const QString& texFileName, int selrow, int docrow) 0276 { 0277 qCDebug(LOG_KILE_PARSER) << logFile << sourceFile; 0278 0279 ParserInput* newItem = new LaTeXOutputParserInput(QUrl::fromLocalFile(logFile), m_ki->extensions(), 0280 sourceFile, 0281 texFileName, selrow, docrow); 0282 addParserInput(newItem); 0283 } 0284 0285 void OutputParserThread::removeFile(const QString& fileName) 0286 { 0287 removeParserInput(QUrl::fromLocalFile(fileName)); 0288 } 0289 0290 } 0291