File: logfiltereddataworkerthread.cpp

package info (click to toggle)
glogg 0.9.0-1
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 832 kB
  • sloc: cpp: 6,649; sh: 66; perl: 32; sed: 25; makefile: 11
file content (277 lines) | stat: -rw-r--r-- 7,966 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
/*
 * Copyright (C) 2009, 2010 Nicolas Bonnefon and other contributors
 *
 * This file is part of glogg.
 *
 * glogg is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * glogg is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with glogg.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <QFile>

#include "log.h"

#include "logfiltereddataworkerthread.h"
#include "logdata.h"

// Number of lines in each chunk to read
const int SearchOperation::nbLinesInChunk = 5000;

void SearchData::getAll( int* length, SearchResultArray* matches,
        qint64* lines) const
{
    QMutexLocker locker( &dataMutex_ );

    *length  = maxLength_;
    *matches = matches_;
    *lines   = nbLinesProcessed_;
}

void SearchData::setAll( int length,
        const SearchResultArray& matches )
{
    QMutexLocker locker( &dataMutex_ );

    maxLength_  = length;
    matches_    = matches;
}

void SearchData::addAll( int length,
        const SearchResultArray& matches, qint64 lines )
{
    QMutexLocker locker( &dataMutex_ );

    maxLength_        = qMax( maxLength_, length );
    matches_          += matches;
    nbLinesProcessed_ = lines;
}

int SearchData::getNbMatches() const
{
    QMutexLocker locker( &dataMutex_ );

    return matches_.length();
}

// This function starts searching from the end since we use it
// to remove the final match.
void SearchData::deleteMatch( qint64 line )
{
    QMutexLocker locker( &dataMutex_ );

    SearchResultArray::iterator i = matches_.end();
    while ( i != matches_.begin() ) {
        i--;
        const int this_line = i->lineNumber();
        if ( this_line == line ) {
            matches_.erase(i);
            break;
        }
        // Exit if we have passed the line number to look for.
        if ( this_line < line )
            break;
    }
}

void SearchData::clear()
{
    QMutexLocker locker( &dataMutex_ );

    maxLength_ = 0;
    matches_.clear();
}



LogFilteredDataWorkerThread::LogFilteredDataWorkerThread(
        const LogData* sourceLogData )
    : QThread(), mutex_(), operationRequestedCond_(), nothingToDoCond_(), searchData_()
{
    terminate_          = false;
    interruptRequested_ = false;
    operationRequested_ = NULL;

    sourceLogData_ = sourceLogData;
}

LogFilteredDataWorkerThread::~LogFilteredDataWorkerThread()
{
    {
        QMutexLocker locker( &mutex_ );
        terminate_ = true;
        operationRequestedCond_.wakeAll();
    }
    wait();
}

void LogFilteredDataWorkerThread::search( const QRegExp& regExp )
{
    QMutexLocker locker( &mutex_ );  // to protect operationRequested_

    LOG(logDEBUG) << "Search requested";

    // If an operation is ongoing, we will block
    while ( (operationRequested_ != NULL) )
        nothingToDoCond_.wait( &mutex_ );

    interruptRequested_ = false;
    operationRequested_ = new FullSearchOperation( sourceLogData_,
            regExp, &interruptRequested_ );
    operationRequestedCond_.wakeAll();
}

void LogFilteredDataWorkerThread::updateSearch( const QRegExp& regExp, qint64 position )
{
    QMutexLocker locker( &mutex_ );  // to protect operationRequested_

    LOG(logDEBUG) << "Search requested";

    // If an operation is ongoing, we will block
    while ( (operationRequested_ != NULL) )
        nothingToDoCond_.wait( &mutex_ );

    interruptRequested_ = false;
    operationRequested_ = new UpdateSearchOperation( sourceLogData_,
            regExp, &interruptRequested_, position );
    operationRequestedCond_.wakeAll();
}

void LogFilteredDataWorkerThread::interrupt()
{
    LOG(logDEBUG) << "Search interruption requested";

    // No mutex here, setting a bool is probably atomic!
    interruptRequested_ = true;

    // We wait for the interruption to be done
    {
        QMutexLocker locker( &mutex_ );
        while ( (operationRequested_ != NULL) )
            nothingToDoCond_.wait( &mutex_ );
    }
}

// This will do an atomic copy of the object
// (hopefully fast as we use Qt containers)
void LogFilteredDataWorkerThread::getSearchResult(
        int* maxLength, SearchResultArray* searchMatches, qint64* nbLinesProcessed )
{
    searchData_.getAll( maxLength, searchMatches, nbLinesProcessed );
}

// This is the thread's main loop
void LogFilteredDataWorkerThread::run()
{
    QMutexLocker locker( &mutex_ );

    forever {
        while ( (terminate_ == false) && (operationRequested_ == NULL) )
            operationRequestedCond_.wait( &mutex_ );
        LOG(logDEBUG) << "Worker thread signaled";

        // Look at what needs to be done
        if ( terminate_ )
            return;      // We must die

        if ( operationRequested_ ) {
            connect( operationRequested_, SIGNAL( searchProgressed( int, int ) ),
                    this, SIGNAL( searchProgressed( int, int ) ) );

            // Run the search operation
            operationRequested_->start( searchData_ );

            LOG(logDEBUG) << "... finished copy in workerThread.";

            emit searchFinished();
            delete operationRequested_;
            operationRequested_ = NULL;
            nothingToDoCond_.wakeAll();
        }
    }
}

//
// Operations implementation
//

SearchOperation::SearchOperation( const LogData* sourceLogData,
        const QRegExp& regExp, bool* interruptRequest )
    : regexp_( regExp ), sourceLogData_( sourceLogData )
{
    interruptRequested_ = interruptRequest;
}

void SearchOperation::doSearch( SearchData& searchData, qint64 initialLine )
{
    const qint64 nbSourceLines = sourceLogData_->getNbLine();
    int maxLength = 0;
    int nbMatches = searchData.getNbMatches();
    SearchResultArray currentList = SearchResultArray();

    for ( qint64 i = initialLine; i < nbSourceLines; i += nbLinesInChunk ) {
        if ( *interruptRequested_ )
            break;

        const int percentage = ( i - initialLine ) * 100 / ( nbSourceLines - initialLine );
        emit searchProgressed( nbMatches, percentage );

        const QStringList lines = sourceLogData_->getLines( i,
                qMin( nbLinesInChunk, (int) ( nbSourceLines - i ) ) );
        LOG(logDEBUG) << "Chunk starting at " << i <<
            ", " << lines.size() << " lines read.";

        int j = 0;
        for ( ; j < lines.size(); j++ ) {
            if ( regexp_.indexIn( lines[j] ) != -1 ) {
                const int length = sourceLogData_->getExpandedLineString(i+j).length();
                if ( length > maxLength )
                    maxLength = length;
                MatchingLine match( i+j );
                currentList.append( match );
                nbMatches++;
            }
        }

        // After each block, copy the data to shared data
        // and update the client
        searchData.addAll( maxLength, currentList, i+j );
        currentList.clear();
    }

    emit searchProgressed( nbMatches, 100 );
}

// Called in the worker thread's context
void FullSearchOperation::start( SearchData& searchData )
{
    // Clear the shared data
    searchData.clear();

    doSearch( searchData, 0 );
}

// Called in the worker thread's context
void UpdateSearchOperation::start( SearchData& searchData )
{
    qint64 initial_line = initialPosition_;

    if ( initial_line >= 1 ) {
        // We need to re-search the last line because it might have
        // been updated (if it was not LF-terminated)
        --initial_line;
        // In case the last line matched, we don't want it to match twice.
        searchData.deleteMatch( initial_line );
    }

    doSearch( searchData, initial_line );
}