Commit 5938f345 authored by Milian Wolff's avatar Milian Wolff
Browse files

Refactor threaded parsing, use long lived worker pattern.

Not only is the code much cleaner, we also have the added advantage
of not spinning up new threads whenever we parse a new file.

Furthermore the signals have been cleaned up to remove the unsafe
accessal of the old ParseThread once it has finished parsing.

BUG: 316307
parent 52dcce05
......@@ -33,7 +33,7 @@
#include "massifdata/filedata.h"
#include "massifdata/parser.h"
#include "massifdata/parsethread.h"
#include "massifdata/parseworker.h"
#include "massifdata/snapshotitem.h"
#include "massifdata/treeleafitem.h"
#include "massifdata/util.h"
......@@ -66,6 +66,7 @@
#include <QInputDialog>
#include <KDebug>
#include <KMessageBox>
#ifdef HAVE_KGRAPHVIEWER
#include <kgraphviewer_interface.h>
......@@ -141,7 +142,7 @@ MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags f)
, m_newAllocator(0)
, m_removeAllocator(0)
, m_shortenTemplates(0)
, m_parseThread(0)
, m_parseWorker(new ParseWorker)
{
ui.setupUi(this);
......@@ -253,10 +254,27 @@ MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags f)
// open page
ui.stackedWidget->setCurrentWidget(ui.openPage);
// threaded parser
QThread* thread = new QThread(this);
thread->start();
m_parseWorker->moveToThread(thread);
connect(m_parseWorker, SIGNAL(finished(KUrl, Massif::FileData*)),
this, SLOT(parserFinished(KUrl, Massif::FileData*)));
connect(m_parseWorker, SIGNAL(error(QString, QString)),
this, SLOT(parserError(QString, QString)));
connect(m_parseWorker, SIGNAL(progressRange(int, int)),
ui.loadingProgress, SLOT(setRange(int,int)));
connect(m_parseWorker, SIGNAL(progress(int)),
ui.loadingProgress, SLOT(setValue(int)));
}
MainWindow::~MainWindow()
{
m_parseWorker->stop();
m_parseWorker->deleteLater();
m_parseWorker->thread()->quit();
m_parseWorker->thread()->wait();
closeFile();
m_recentFiles->saveEntries(KGlobal::config()->group( QString() ));
}
......@@ -421,12 +439,7 @@ void MainWindow::reload()
void MainWindow::stopParser()
{
if (m_parseThread) {
m_parseThread->stop();
disconnect(m_parseThread, 0, this, 0);
m_parseThread = 0;
}
m_parseWorker->stop();
m_stopParser->setEnabled(false);
ui.stackedWidget->setCurrentWidget(ui.openPage);
}
......@@ -445,36 +458,16 @@ void MainWindow::openFile(const KUrl& file)
ui.loadingProgress->setRange(0, 0);
ui.loadingMessage->setText(i18n("loading file <i>%1</i>...", file.pathOrUrl()));
m_parseThread = new ParseThread(this);
connect(m_parseThread, SIGNAL(finished(ParseThread*, FileData*)),
this, SLOT(parserFinished(ParseThread*, FileData*)));
connect(m_parseThread, SIGNAL(finished()),
m_parseThread, SLOT(deleteLater()));
connect(m_parseThread, SIGNAL(progressRange(int, int)),
ui.loadingProgress, SLOT(setRange(int,int)));
connect(m_parseThread, SIGNAL(progress(int)),
ui.loadingProgress, SLOT(setValue(int)));
m_parseThread->startParsing(file, m_allocatorModel->stringList());
m_parseWorker->parse(file, m_allocatorModel->stringList());
}
void MainWindow::parserFinished(ParseThread* thread, FileData* data)
void MainWindow::parserFinished(const KUrl& file, FileData* data)
{
// give the progress bar one last chance to update
QApplication::processEvents();
Q_ASSERT(thread == m_parseThread);
m_parseThread = 0;
QScopedPointer<ParseThread> threadHouseholder(thread);
if (!data) {
thread->showError(this);
return;
}
m_data = data;
m_currentFile = thread->file();
m_currentFile = file;
Q_ASSERT(m_data->peak());
......@@ -586,6 +579,11 @@ void MainWindow::parserFinished(ParseThread* thread, FileData* data)
ui.stackedWidget->setCurrentWidget(ui.displayPage);
}
void MainWindow::parserError(const QString& title, const QString& error)
{
KMessageBox::error(this, error, title);
}
void MainWindow::updateHeader()
{
const QString app = m_data->cmd().split(' ', QString::SkipEmptyParts).first();
......
......@@ -54,7 +54,7 @@ class KGraphViewerInterface;
namespace Massif {
class ParseThread;
class ParseWorker;
class FileData;
class DetailedCostModel;
class TotalCostModel;
......@@ -141,7 +141,8 @@ private slots:
void slotShortenTemplates(bool);
void stopParser();
void parserFinished(ParseThread* thread, FileData* data);
void parserFinished(const KUrl& file, Massif::FileData* data);
void parserError(const QString& title, const QString& error);
private:
void getDotGraph(QPair<TreeLeafItem*, SnapshotItem*> item);
......@@ -194,7 +195,7 @@ private:
KAction* m_shortenTemplates;
ParseThread* m_parseThread;
ParseWorker* m_parseWorker;
};
}
......
......@@ -9,7 +9,7 @@ set(massifdata_SRCS
treeleafitem.cpp
parser.cpp
parserprivate.cpp
parsethread.cpp
parseworker.cpp
util.cpp
)
......
......@@ -20,98 +20,81 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "parsethread.h"
#include "parseworker.h"
#include "parser.h"
#include "filedata.h"
#include <KUrl>
#include <KFilterDev>
#include <KIO/NetAccess>
#include <KTemporaryFile>
#include <KLocalizedString>
#include <KMessageBox>
namespace Massif {
ParseThread::ParseThread(QObject* parent)
: QThread(parent)
ParseWorker::ParseWorker(QObject* parent)
: QObject(parent)
{
}
void ParseThread::startParsing(const KUrl& url, const QStringList& allocators)
void ParseWorker::parse(const KUrl& url, const QStringList& allocators)
{
Q_ASSERT(m_url.isEmpty());
m_url = url;
m_allocators = allocators;
start();
}
// process in background thread
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "parse", Q_ARG(KUrl, url), Q_ARG(QStringList, allocators));
return;
}
void ParseThread::run()
{
m_shouldStop = 0;
QString file;
if (!m_url.isLocalFile()) {
if (!KIO::NetAccess::download(m_url, file, 0)) {
setError(i18n("Download Failed"),
i18n("Failed to download remote massif data file <i>%1</i>.", m_url.pathOrUrl()));
if (!url.isLocalFile()) {
if (!KIO::NetAccess::download(url, file, 0)) {
emit error(i18n("Download Failed"),
i18n("Failed to download remote massif data file <i>%1</i>.", url.pathOrUrl()));
return;
}
} else {
file = m_url.toLocalFile();
file = url.toLocalFile();
}
QScopedPointer<QIODevice> device(KFilterDev::deviceForFile(file));
if (!device->open(QIODevice::ReadOnly)) {
setError(i18n("Read Failed"),
i18n("Could not open file <i>%1</i> for reading.", file));
emit error(i18n("Read Failed"),
i18n("Could not open file <i>%1</i> for reading.", file));
return;
}
Parser p;
emit progressRange(0, 100);
connect(&p, SIGNAL(progress(int)), this, SIGNAL(progress(int)));
QScopedPointer<FileData> data(p.parse(device.data(), m_allocators, &m_shouldStop));
QScopedPointer<FileData> data(p.parse(device.data(), allocators, &m_shouldStop));
if (!data) {
setError(i18n("Parser Failed"),
i18n("Could not parse file <i>%1</i>.<br>"
"Parse error in line %2:<br>%3",
m_url.pathOrUrl(), p.errorLine() + 1,
QString::fromLatin1(p.errorLineString())));
if (!m_shouldStop) {
emit error(i18n("Parser Failed"),
i18n("Could not parse file <i>%1</i>.<br>"
"Parse error in line %2:<br>%3",
url.pathOrUrl(), p.errorLine() + 1,
QString::fromLatin1(p.errorLineString())));
}
return;
} else if (data->snapshots().isEmpty()) {
setError(i18n("Empty data file <i>%1</i>.", m_url.pathOrUrl()), i18n("Empty Data File"));
emit error(i18n("Empty data file <i>%1</i>.", url.pathOrUrl()),
i18n("Empty Data File"));
return;
}
emit progress(100);
// success!
emit finished(this, data.take());
}
void ParseThread::showError(QWidget* parent) const
{
KMessageBox::error(parent, m_errorBody, m_errorTitle);
emit finished(url, data.take());
}
void ParseThread::stop()
void ParseWorker::stop()
{
m_shouldStop = 1;
}
KUrl ParseThread::file() const
{
return m_url;
}
void ParseThread::setError ( const QString& title, const QString& body )
{
m_errorTitle = title;
m_errorBody = body;
emit finished(this, 0);
}
}
#include "parsethread.moc"
#include "parseworker.moc"
......@@ -32,42 +32,36 @@ namespace Massif {
class FileData;
class ParseThread : public QThread
class ParseWorker : public QObject
{
Q_OBJECT
public:
explicit ParseThread(QObject* parent = 0);
explicit ParseWorker(QObject* parent = 0);
void startParsing(const KUrl& url, const QStringList& allocators);
void showError(QWidget* parent) const;
void stop();
KUrl file() const;
public slots:
void parse(const KUrl& url, const QStringList& allocators);
signals:
void finished(ParseThread* thread, FileData* data);
/**
* Emitted once a file was properly parsed.
*/
void finished(const KUrl& url, Massif::FileData* data);
/**
* Emitted if a file could not be parsed.
*/
void error(const QString& error, const QString& message);
void progressRange(int min, int max);
void progress(int value);
public slots:
void stop();
protected:
virtual void run();
private:
void setError(const QString& title, const QString& body);
private:
KUrl m_url;
QStringList m_allocators;
QAtomicInt m_shouldStop;
QString m_errorTitle;
QString m_errorBody;
};
}
#endif // MASSIFDATA_PARSETHREAD_H
\ No newline at end of file
#endif // MASSIFDATA_PARSETHREAD_H
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment