xref: /glogg/src/data/logfiltereddataworkerthread.cpp (revision c59cadb3bc56e902de41bd6b429f9d7bc056088a)
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 &regExp, 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