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