xref: /glogg/src/data/logfiltereddataworkerthread.cpp (revision bb02e0acf44ddb4e4f83d6127a1e488789162922)
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     matches_.clear();
92 }
93 
94 
95 
96 LogFilteredDataWorkerThread::LogFilteredDataWorkerThread(
97         const LogData* sourceLogData )
98     : QThread(), mutex_(), operationRequestedCond_(), nothingToDoCond_(), searchData_()
99 {
100     terminate_          = false;
101     interruptRequested_ = false;
102     operationRequested_ = NULL;
103 
104     sourceLogData_ = sourceLogData;
105 }
106 
107 LogFilteredDataWorkerThread::~LogFilteredDataWorkerThread()
108 {
109     {
110         QMutexLocker locker( &mutex_ );
111         terminate_ = true;
112         operationRequestedCond_.wakeAll();
113     }
114     wait();
115 }
116 
117 void LogFilteredDataWorkerThread::search( const QRegExp& regExp )
118 {
119     QMutexLocker locker( &mutex_ );  // to protect operationRequested_
120 
121     LOG(logDEBUG) << "Search requested";
122 
123     // If an operation is ongoing, we will block
124     while ( (operationRequested_ != NULL) )
125         nothingToDoCond_.wait( &mutex_ );
126 
127     interruptRequested_ = false;
128     operationRequested_ = new FullSearchOperation( sourceLogData_,
129             regExp, &interruptRequested_ );
130     operationRequestedCond_.wakeAll();
131 }
132 
133 void LogFilteredDataWorkerThread::updateSearch( const QRegExp& regExp, qint64 position )
134 {
135     QMutexLocker locker( &mutex_ );  // to protect operationRequested_
136 
137     LOG(logDEBUG) << "Search requested";
138 
139     // If an operation is ongoing, we will block
140     while ( (operationRequested_ != NULL) )
141         nothingToDoCond_.wait( &mutex_ );
142 
143     interruptRequested_ = false;
144     operationRequested_ = new UpdateSearchOperation( sourceLogData_,
145             regExp, &interruptRequested_, position );
146     operationRequestedCond_.wakeAll();
147 }
148 
149 void LogFilteredDataWorkerThread::interrupt()
150 {
151     LOG(logDEBUG) << "Search interruption requested";
152 
153     // No mutex here, setting a bool is probably atomic!
154     interruptRequested_ = true;
155 
156     // We wait for the interruption to be done
157     {
158         QMutexLocker locker( &mutex_ );
159         while ( (operationRequested_ != NULL) )
160             nothingToDoCond_.wait( &mutex_ );
161     }
162 }
163 
164 // This will do an atomic copy of the object
165 // (hopefully fast as we use Qt containers)
166 void LogFilteredDataWorkerThread::getSearchResult(
167         int* maxLength, SearchResultArray* searchMatches, qint64* nbLinesProcessed )
168 {
169     searchData_.getAll( maxLength, searchMatches, nbLinesProcessed );
170 }
171 
172 // This is the thread's main loop
173 void LogFilteredDataWorkerThread::run()
174 {
175     QMutexLocker locker( &mutex_ );
176 
177     forever {
178         while ( (terminate_ == false) && (operationRequested_ == NULL) )
179             operationRequestedCond_.wait( &mutex_ );
180         LOG(logDEBUG) << "Worker thread signaled";
181 
182         // Look at what needs to be done
183         if ( terminate_ )
184             return;      // We must die
185 
186         if ( operationRequested_ ) {
187             connect( operationRequested_, SIGNAL( searchProgressed( int, int ) ),
188                     this, SIGNAL( searchProgressed( int, int ) ) );
189 
190             // Run the search operation
191             operationRequested_->start( searchData_ );
192 
193             LOG(logDEBUG) << "... finished copy in workerThread.";
194 
195             emit searchFinished();
196             delete operationRequested_;
197             operationRequested_ = NULL;
198             nothingToDoCond_.wakeAll();
199         }
200     }
201 }
202 
203 //
204 // Operations implementation
205 //
206 
207 SearchOperation::SearchOperation( const LogData* sourceLogData,
208         const QRegExp& regExp, bool* interruptRequest )
209     : regexp_( regExp ), sourceLogData_( sourceLogData )
210 {
211     interruptRequested_ = interruptRequest;
212 }
213 
214 void SearchOperation::doSearch( SearchData& searchData, qint64 initialLine )
215 {
216     const qint64 nbSourceLines = sourceLogData_->getNbLine();
217     int maxLength = 0;
218     int nbMatches = searchData.getNbMatches();
219     SearchResultArray currentList = SearchResultArray();
220 
221     for ( qint64 i = initialLine; i < nbSourceLines; i += nbLinesInChunk ) {
222         if ( *interruptRequested_ )
223             break;
224 
225         const int percentage = ( i - initialLine ) * 100 / ( nbSourceLines - initialLine );
226         emit searchProgressed( nbMatches, percentage );
227 
228         const QStringList lines = sourceLogData_->getLines( i,
229                 qMin( nbLinesInChunk, (int) ( nbSourceLines - i ) ) );
230         LOG(logDEBUG) << "Chunk starting at " << i <<
231             ", " << lines.size() << " lines read.";
232 
233         int j = 0;
234         for ( ; j < lines.size(); j++ ) {
235             if ( regexp_.indexIn( lines[j] ) != -1 ) {
236                 const int length = sourceLogData_->getExpandedLineString(i+j).length();
237                 if ( length > maxLength )
238                     maxLength = length;
239                 MatchingLine match( i+j );
240                 currentList.append( match );
241                 nbMatches++;
242             }
243         }
244 
245         // After each block, copy the data to shared data
246         // and update the client
247         searchData.addAll( maxLength, currentList, i+j );
248         currentList.clear();
249     }
250 
251     emit searchProgressed( nbMatches, 100 );
252 }
253 
254 // Called in the worker thread's context
255 void FullSearchOperation::start( SearchData& searchData )
256 {
257     // Clear the shared data
258     searchData.clear();
259 
260     doSearch( searchData, 0 );
261 }
262 
263 // Called in the worker thread's context
264 void UpdateSearchOperation::start( SearchData& searchData )
265 {
266     qint64 initial_line = initialPosition_;
267 
268     if ( initial_line >= 1 ) {
269         // We need to re-search the last line because it might have
270         // been updated (if it was not LF-terminated)
271         --initial_line;
272         // In case the last line matched, we don't want it to match twice.
273         searchData.deleteMatch( initial_line );
274     }
275 
276     doSearch( searchData, initial_line );
277 }
278