1bb02e0acSNicolas Bonnefon /*
2bb02e0acSNicolas Bonnefon * Copyright (C) 2009, 2010 Nicolas Bonnefon and other contributors
3bb02e0acSNicolas Bonnefon *
4bb02e0acSNicolas Bonnefon * This file is part of glogg.
5bb02e0acSNicolas Bonnefon *
6bb02e0acSNicolas Bonnefon * glogg is free software: you can redistribute it and/or modify
7bb02e0acSNicolas Bonnefon * it under the terms of the GNU General Public License as published by
8bb02e0acSNicolas Bonnefon * the Free Software Foundation, either version 3 of the License, or
9bb02e0acSNicolas Bonnefon * (at your option) any later version.
10bb02e0acSNicolas Bonnefon *
11bb02e0acSNicolas Bonnefon * glogg is distributed in the hope that it will be useful,
12bb02e0acSNicolas Bonnefon * but WITHOUT ANY WARRANTY; without even the implied warranty of
13bb02e0acSNicolas Bonnefon * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14bb02e0acSNicolas Bonnefon * GNU General Public License for more details.
15bb02e0acSNicolas Bonnefon *
16bb02e0acSNicolas Bonnefon * You should have received a copy of the GNU General Public License
17bb02e0acSNicolas Bonnefon * along with glogg. If not, see <http://www.gnu.org/licenses/>.
18bb02e0acSNicolas Bonnefon */
19bb02e0acSNicolas Bonnefon
20bb02e0acSNicolas Bonnefon #include <QFile>
21bb02e0acSNicolas Bonnefon
22bb02e0acSNicolas Bonnefon #include "log.h"
23bb02e0acSNicolas Bonnefon
24bb02e0acSNicolas Bonnefon #include "logfiltereddataworkerthread.h"
25bb02e0acSNicolas Bonnefon #include "logdata.h"
26bb02e0acSNicolas Bonnefon
27bb02e0acSNicolas Bonnefon // Number of lines in each chunk to read
28bb02e0acSNicolas Bonnefon const int SearchOperation::nbLinesInChunk = 5000;
29bb02e0acSNicolas Bonnefon
getAll(int * length,SearchResultArray * matches,qint64 * lines) const30bb02e0acSNicolas Bonnefon void SearchData::getAll( int* length, SearchResultArray* matches,
31bb02e0acSNicolas Bonnefon qint64* lines) const
32bb02e0acSNicolas Bonnefon {
33bb02e0acSNicolas Bonnefon QMutexLocker locker( &dataMutex_ );
34bb02e0acSNicolas Bonnefon
35bb02e0acSNicolas Bonnefon *length = maxLength_;
36bb02e0acSNicolas Bonnefon *lines = nbLinesProcessed_;
37ced968a9SNicolas Bonnefon
38ced968a9SNicolas Bonnefon // This is a copy (potentially slow)
39ced968a9SNicolas Bonnefon *matches = matches_;
40bb02e0acSNicolas Bonnefon }
41bb02e0acSNicolas Bonnefon
setAll(int length,SearchResultArray && matches)42bb02e0acSNicolas Bonnefon void SearchData::setAll( int length,
43ced968a9SNicolas Bonnefon SearchResultArray&& matches )
44bb02e0acSNicolas Bonnefon {
45bb02e0acSNicolas Bonnefon QMutexLocker locker( &dataMutex_ );
46bb02e0acSNicolas Bonnefon
47bb02e0acSNicolas Bonnefon maxLength_ = length;
48bb02e0acSNicolas Bonnefon matches_ = matches;
49bb02e0acSNicolas Bonnefon }
50bb02e0acSNicolas Bonnefon
addAll(int length,const SearchResultArray & matches,LineNumber lines)51bb02e0acSNicolas Bonnefon void SearchData::addAll( int length,
52ced968a9SNicolas Bonnefon const SearchResultArray& matches, LineNumber lines )
53bb02e0acSNicolas Bonnefon {
54bb02e0acSNicolas Bonnefon QMutexLocker locker( &dataMutex_ );
55bb02e0acSNicolas Bonnefon
56bb02e0acSNicolas Bonnefon maxLength_ = qMax( maxLength_, length );
57bb02e0acSNicolas Bonnefon nbLinesProcessed_ = lines;
58ced968a9SNicolas Bonnefon
59ced968a9SNicolas Bonnefon // This does a copy as we want the final array to be
60ced968a9SNicolas Bonnefon // linear.
61ced968a9SNicolas Bonnefon matches_.insert( std::end( matches_ ),
62ced968a9SNicolas Bonnefon std::begin( matches ), std::end( matches ) );
63bb02e0acSNicolas Bonnefon }
64bb02e0acSNicolas Bonnefon
getNbMatches() const65ced968a9SNicolas Bonnefon LineNumber SearchData::getNbMatches() const
66bb02e0acSNicolas Bonnefon {
67bb02e0acSNicolas Bonnefon QMutexLocker locker( &dataMutex_ );
68bb02e0acSNicolas Bonnefon
69ced968a9SNicolas Bonnefon return matches_.size();
70bb02e0acSNicolas Bonnefon }
71bb02e0acSNicolas Bonnefon
72bb02e0acSNicolas Bonnefon // This function starts searching from the end since we use it
73bb02e0acSNicolas Bonnefon // to remove the final match.
deleteMatch(LineNumber line)74ced968a9SNicolas Bonnefon void SearchData::deleteMatch( LineNumber line )
75bb02e0acSNicolas Bonnefon {
76bb02e0acSNicolas Bonnefon QMutexLocker locker( &dataMutex_ );
77bb02e0acSNicolas Bonnefon
78bb02e0acSNicolas Bonnefon SearchResultArray::iterator i = matches_.end();
79bb02e0acSNicolas Bonnefon while ( i != matches_.begin() ) {
80bb02e0acSNicolas Bonnefon i--;
81ced968a9SNicolas Bonnefon const LineNumber this_line = i->lineNumber();
82bb02e0acSNicolas Bonnefon if ( this_line == line ) {
83bb02e0acSNicolas Bonnefon matches_.erase(i);
84bb02e0acSNicolas Bonnefon break;
85bb02e0acSNicolas Bonnefon }
86bb02e0acSNicolas Bonnefon // Exit if we have passed the line number to look for.
87bb02e0acSNicolas Bonnefon if ( this_line < line )
88bb02e0acSNicolas Bonnefon break;
89bb02e0acSNicolas Bonnefon }
90bb02e0acSNicolas Bonnefon }
91bb02e0acSNicolas Bonnefon
clear()92bb02e0acSNicolas Bonnefon void SearchData::clear()
93bb02e0acSNicolas Bonnefon {
94bb02e0acSNicolas Bonnefon QMutexLocker locker( &dataMutex_ );
95bb02e0acSNicolas Bonnefon
96bb02e0acSNicolas Bonnefon maxLength_ = 0;
97721a2433SNicolas Bonnefon nbLinesProcessed_ = 0;
98bb02e0acSNicolas Bonnefon matches_.clear();
99bb02e0acSNicolas Bonnefon }
100bb02e0acSNicolas Bonnefon
LogFilteredDataWorkerThread(const LogData * sourceLogData)101bb02e0acSNicolas Bonnefon LogFilteredDataWorkerThread::LogFilteredDataWorkerThread(
102bb02e0acSNicolas Bonnefon const LogData* sourceLogData )
103bb02e0acSNicolas Bonnefon : QThread(), mutex_(), operationRequestedCond_(), nothingToDoCond_(), searchData_()
104bb02e0acSNicolas Bonnefon {
105bb02e0acSNicolas Bonnefon terminate_ = false;
106bb02e0acSNicolas Bonnefon interruptRequested_ = false;
107bb02e0acSNicolas Bonnefon operationRequested_ = NULL;
108bb02e0acSNicolas Bonnefon
109bb02e0acSNicolas Bonnefon sourceLogData_ = sourceLogData;
110bb02e0acSNicolas Bonnefon }
111bb02e0acSNicolas Bonnefon
~LogFilteredDataWorkerThread()112bb02e0acSNicolas Bonnefon LogFilteredDataWorkerThread::~LogFilteredDataWorkerThread()
113bb02e0acSNicolas Bonnefon {
114bb02e0acSNicolas Bonnefon {
115bb02e0acSNicolas Bonnefon QMutexLocker locker( &mutex_ );
116bb02e0acSNicolas Bonnefon terminate_ = true;
117bb02e0acSNicolas Bonnefon operationRequestedCond_.wakeAll();
118bb02e0acSNicolas Bonnefon }
119bb02e0acSNicolas Bonnefon wait();
120bb02e0acSNicolas Bonnefon }
121bb02e0acSNicolas Bonnefon
search(const QRegularExpression & regExp)1224fb0346eSAnton Filimonov void LogFilteredDataWorkerThread::search( const QRegularExpression& regExp )
123bb02e0acSNicolas Bonnefon {
124bb02e0acSNicolas Bonnefon QMutexLocker locker( &mutex_ ); // to protect operationRequested_
125bb02e0acSNicolas Bonnefon
126bb02e0acSNicolas Bonnefon LOG(logDEBUG) << "Search requested";
127bb02e0acSNicolas Bonnefon
128bb02e0acSNicolas Bonnefon // If an operation is ongoing, we will block
129bb02e0acSNicolas Bonnefon while ( (operationRequested_ != NULL) )
130bb02e0acSNicolas Bonnefon nothingToDoCond_.wait( &mutex_ );
131bb02e0acSNicolas Bonnefon
132bb02e0acSNicolas Bonnefon interruptRequested_ = false;
133bb02e0acSNicolas Bonnefon operationRequested_ = new FullSearchOperation( sourceLogData_,
134bb02e0acSNicolas Bonnefon regExp, &interruptRequested_ );
135bb02e0acSNicolas Bonnefon operationRequestedCond_.wakeAll();
136bb02e0acSNicolas Bonnefon }
137bb02e0acSNicolas Bonnefon
updateSearch(const QRegularExpression & regExp,qint64 position)1384fb0346eSAnton Filimonov void LogFilteredDataWorkerThread::updateSearch(const QRegularExpression ®Exp, qint64 position )
139bb02e0acSNicolas Bonnefon {
140bb02e0acSNicolas Bonnefon QMutexLocker locker( &mutex_ ); // to protect operationRequested_
141bb02e0acSNicolas Bonnefon
142bb02e0acSNicolas Bonnefon LOG(logDEBUG) << "Search requested";
143bb02e0acSNicolas Bonnefon
144bb02e0acSNicolas Bonnefon // If an operation is ongoing, we will block
145bb02e0acSNicolas Bonnefon while ( (operationRequested_ != NULL) )
146bb02e0acSNicolas Bonnefon nothingToDoCond_.wait( &mutex_ );
147bb02e0acSNicolas Bonnefon
148bb02e0acSNicolas Bonnefon interruptRequested_ = false;
149bb02e0acSNicolas Bonnefon operationRequested_ = new UpdateSearchOperation( sourceLogData_,
150bb02e0acSNicolas Bonnefon regExp, &interruptRequested_, position );
151bb02e0acSNicolas Bonnefon operationRequestedCond_.wakeAll();
152bb02e0acSNicolas Bonnefon }
153bb02e0acSNicolas Bonnefon
interrupt()154bb02e0acSNicolas Bonnefon void LogFilteredDataWorkerThread::interrupt()
155bb02e0acSNicolas Bonnefon {
156bb02e0acSNicolas Bonnefon LOG(logDEBUG) << "Search interruption requested";
157bb02e0acSNicolas Bonnefon
158bb02e0acSNicolas Bonnefon // No mutex here, setting a bool is probably atomic!
159bb02e0acSNicolas Bonnefon interruptRequested_ = true;
160bb02e0acSNicolas Bonnefon
161bb02e0acSNicolas Bonnefon // We wait for the interruption to be done
162bb02e0acSNicolas Bonnefon {
163bb02e0acSNicolas Bonnefon QMutexLocker locker( &mutex_ );
164bb02e0acSNicolas Bonnefon while ( (operationRequested_ != NULL) )
165bb02e0acSNicolas Bonnefon nothingToDoCond_.wait( &mutex_ );
166bb02e0acSNicolas Bonnefon }
167bb02e0acSNicolas Bonnefon }
168bb02e0acSNicolas Bonnefon
169bb02e0acSNicolas Bonnefon // This will do an atomic copy of the object
getSearchResult(int * maxLength,SearchResultArray * searchMatches,qint64 * nbLinesProcessed)170bb02e0acSNicolas Bonnefon void LogFilteredDataWorkerThread::getSearchResult(
171bb02e0acSNicolas Bonnefon int* maxLength, SearchResultArray* searchMatches, qint64* nbLinesProcessed )
172bb02e0acSNicolas Bonnefon {
173bb02e0acSNicolas Bonnefon searchData_.getAll( maxLength, searchMatches, nbLinesProcessed );
174bb02e0acSNicolas Bonnefon }
175bb02e0acSNicolas Bonnefon
176bb02e0acSNicolas Bonnefon // This is the thread's main loop
run()177bb02e0acSNicolas Bonnefon void LogFilteredDataWorkerThread::run()
178bb02e0acSNicolas Bonnefon {
179bb02e0acSNicolas Bonnefon QMutexLocker locker( &mutex_ );
180bb02e0acSNicolas Bonnefon
181bb02e0acSNicolas Bonnefon forever {
182bb02e0acSNicolas Bonnefon while ( (terminate_ == false) && (operationRequested_ == NULL) )
183bb02e0acSNicolas Bonnefon operationRequestedCond_.wait( &mutex_ );
184bb02e0acSNicolas Bonnefon LOG(logDEBUG) << "Worker thread signaled";
185bb02e0acSNicolas Bonnefon
186bb02e0acSNicolas Bonnefon // Look at what needs to be done
187bb02e0acSNicolas Bonnefon if ( terminate_ )
188bb02e0acSNicolas Bonnefon return; // We must die
189bb02e0acSNicolas Bonnefon
190bb02e0acSNicolas Bonnefon if ( operationRequested_ ) {
191*c59cadb3SNicolas Bonnefon connect( operationRequested_, SIGNAL( searchProgressed( int, int, qint64 ) ),
192*c59cadb3SNicolas Bonnefon this, SIGNAL( searchProgressed( int, int, qint64 ) ) );
193bb02e0acSNicolas Bonnefon
194bb02e0acSNicolas Bonnefon // Run the search operation
195bb02e0acSNicolas Bonnefon operationRequested_->start( searchData_ );
196bb02e0acSNicolas Bonnefon
197bb02e0acSNicolas Bonnefon LOG(logDEBUG) << "... finished copy in workerThread.";
198bb02e0acSNicolas Bonnefon
199bb02e0acSNicolas Bonnefon emit searchFinished();
200bb02e0acSNicolas Bonnefon delete operationRequested_;
201bb02e0acSNicolas Bonnefon operationRequested_ = NULL;
202bb02e0acSNicolas Bonnefon nothingToDoCond_.wakeAll();
203bb02e0acSNicolas Bonnefon }
204bb02e0acSNicolas Bonnefon }
205bb02e0acSNicolas Bonnefon }
206bb02e0acSNicolas Bonnefon
207bb02e0acSNicolas Bonnefon //
208bb02e0acSNicolas Bonnefon // Operations implementation
209bb02e0acSNicolas Bonnefon //
210bb02e0acSNicolas Bonnefon
SearchOperation(const LogData * sourceLogData,const QRegularExpression & regExp,bool * interruptRequest)211bb02e0acSNicolas Bonnefon SearchOperation::SearchOperation( const LogData* sourceLogData,
2124fb0346eSAnton Filimonov const QRegularExpression& regExp, bool* interruptRequest )
213bb02e0acSNicolas Bonnefon : regexp_( regExp ), sourceLogData_( sourceLogData )
214bb02e0acSNicolas Bonnefon {
215bb02e0acSNicolas Bonnefon interruptRequested_ = interruptRequest;
216bb02e0acSNicolas Bonnefon }
217bb02e0acSNicolas Bonnefon
doSearch(SearchData & searchData,qint64 initialLine)218bb02e0acSNicolas Bonnefon void SearchOperation::doSearch( SearchData& searchData, qint64 initialLine )
219bb02e0acSNicolas Bonnefon {
220bb02e0acSNicolas Bonnefon const qint64 nbSourceLines = sourceLogData_->getNbLine();
221bb02e0acSNicolas Bonnefon int maxLength = 0;
222bb02e0acSNicolas Bonnefon int nbMatches = searchData.getNbMatches();
223bb02e0acSNicolas Bonnefon SearchResultArray currentList = SearchResultArray();
224bb02e0acSNicolas Bonnefon
225ced968a9SNicolas Bonnefon // Ensure no re-alloc will be done
226ced968a9SNicolas Bonnefon currentList.reserve( nbLinesInChunk );
227ced968a9SNicolas Bonnefon
228721a2433SNicolas Bonnefon LOG(logDEBUG) << "Searching from line " << initialLine << " to " << nbSourceLines;
229721a2433SNicolas Bonnefon
230bb02e0acSNicolas Bonnefon for ( qint64 i = initialLine; i < nbSourceLines; i += nbLinesInChunk ) {
231bb02e0acSNicolas Bonnefon if ( *interruptRequested_ )
232bb02e0acSNicolas Bonnefon break;
233bb02e0acSNicolas Bonnefon
234bb02e0acSNicolas Bonnefon const int percentage = ( i - initialLine ) * 100 / ( nbSourceLines - initialLine );
235*c59cadb3SNicolas Bonnefon emit searchProgressed( nbMatches, percentage, initialLine );
236bb02e0acSNicolas Bonnefon
237bb02e0acSNicolas Bonnefon const QStringList lines = sourceLogData_->getLines( i,
238bb02e0acSNicolas Bonnefon qMin( nbLinesInChunk, (int) ( nbSourceLines - i ) ) );
239bb02e0acSNicolas Bonnefon LOG(logDEBUG) << "Chunk starting at " << i <<
240bb02e0acSNicolas Bonnefon ", " << lines.size() << " lines read.";
241bb02e0acSNicolas Bonnefon
242bb02e0acSNicolas Bonnefon int j = 0;
243bb02e0acSNicolas Bonnefon for ( ; j < lines.size(); j++ ) {
2444fb0346eSAnton Filimonov if ( regexp_.match( lines[j] ).hasMatch() ) {
245ced968a9SNicolas Bonnefon // FIXME: increase perf by removing temporary
246bb02e0acSNicolas Bonnefon const int length = sourceLogData_->getExpandedLineString(i+j).length();
247bb02e0acSNicolas Bonnefon if ( length > maxLength )
248bb02e0acSNicolas Bonnefon maxLength = length;
249ced968a9SNicolas Bonnefon currentList.push_back( MatchingLine( i+j ) );
250bb02e0acSNicolas Bonnefon nbMatches++;
251bb02e0acSNicolas Bonnefon }
252bb02e0acSNicolas Bonnefon }
253bb02e0acSNicolas Bonnefon
254bb02e0acSNicolas Bonnefon // After each block, copy the data to shared data
255bb02e0acSNicolas Bonnefon // and update the client
256bb02e0acSNicolas Bonnefon searchData.addAll( maxLength, currentList, i+j );
257bb02e0acSNicolas Bonnefon currentList.clear();
258bb02e0acSNicolas Bonnefon }
259bb02e0acSNicolas Bonnefon
260*c59cadb3SNicolas Bonnefon emit searchProgressed( nbMatches, 100, initialLine );
261bb02e0acSNicolas Bonnefon }
262bb02e0acSNicolas Bonnefon
263bb02e0acSNicolas Bonnefon // Called in the worker thread's context
start(SearchData & searchData)264bb02e0acSNicolas Bonnefon void FullSearchOperation::start( SearchData& searchData )
265bb02e0acSNicolas Bonnefon {
266bb02e0acSNicolas Bonnefon // Clear the shared data
267bb02e0acSNicolas Bonnefon searchData.clear();
268bb02e0acSNicolas Bonnefon
269bb02e0acSNicolas Bonnefon doSearch( searchData, 0 );
270bb02e0acSNicolas Bonnefon }
271bb02e0acSNicolas Bonnefon
272bb02e0acSNicolas Bonnefon // Called in the worker thread's context
start(SearchData & searchData)273bb02e0acSNicolas Bonnefon void UpdateSearchOperation::start( SearchData& searchData )
274bb02e0acSNicolas Bonnefon {
275bb02e0acSNicolas Bonnefon qint64 initial_line = initialPosition_;
276bb02e0acSNicolas Bonnefon
277bb02e0acSNicolas Bonnefon if ( initial_line >= 1 ) {
278bb02e0acSNicolas Bonnefon // We need to re-search the last line because it might have
279bb02e0acSNicolas Bonnefon // been updated (if it was not LF-terminated)
280bb02e0acSNicolas Bonnefon --initial_line;
281bb02e0acSNicolas Bonnefon // In case the last line matched, we don't want it to match twice.
282bb02e0acSNicolas Bonnefon searchData.deleteMatch( initial_line );
283bb02e0acSNicolas Bonnefon }
284bb02e0acSNicolas Bonnefon
285bb02e0acSNicolas Bonnefon doSearch( searchData, initial_line );
286bb02e0acSNicolas Bonnefon }
287