/* * 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 . */ #include #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_; *lines = nbLinesProcessed_; // This is a copy (potentially slow) *matches = matches_; } void SearchData::setAll( int length, SearchResultArray&& matches ) { QMutexLocker locker( &dataMutex_ ); maxLength_ = length; matches_ = matches; } void SearchData::addAll( int length, const SearchResultArray& matches, LineNumber lines ) { QMutexLocker locker( &dataMutex_ ); maxLength_ = qMax( maxLength_, length ); nbLinesProcessed_ = lines; // This does a copy as we want the final array to be // linear. matches_.insert( std::end( matches_ ), std::begin( matches ), std::end( matches ) ); } LineNumber SearchData::getNbMatches() const { QMutexLocker locker( &dataMutex_ ); return matches_.size(); } // This function starts searching from the end since we use it // to remove the final match. void SearchData::deleteMatch( LineNumber line ) { QMutexLocker locker( &dataMutex_ ); SearchResultArray::iterator i = matches_.end(); while ( i != matches_.begin() ) { i--; const LineNumber 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; nbLinesProcessed_ = 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 QRegularExpression& 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 QRegularExpression ®Exp, 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 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 QRegularExpression& 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(); // Ensure no re-alloc will be done currentList.reserve( nbLinesInChunk ); LOG(logDEBUG) << "Searching from line " << initialLine << " to " << nbSourceLines; 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_.match( lines[j] ).hasMatch() ) { // FIXME: increase perf by removing temporary const int length = sourceLogData_->getExpandedLineString(i+j).length(); if ( length > maxLength ) maxLength = length; currentList.push_back( MatchingLine( i+j ) ); 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 ); }