#include "mercurialjobrunner.h" #include "mercurialplugin.h" #include "mercurialoutputwindow.h" #include "constants.h" #include "mercurialsettings.h" #include <vcsbase/vcsbaseeditor.h> #include <QtCore/QProcess> #include <QtCore/QTime> #include <QtCore/QString> #include <QtCore/QSettings> using namespace Mercurial::Internal; using namespace Mercurial; HgTask::HgTask(const QString &repositoryRoot, QStringList &arguments, bool emitRaw) : m_repositoryRoot(repositoryRoot), arguments(arguments), emitRaw(emitRaw), editor(0) { } HgTask::HgTask(const QString &repositoryRoot, QStringList &arguments, VCSBase::VCSBaseEditor *editor) : m_repositoryRoot(repositoryRoot), arguments(arguments), emitRaw(false), editor(editor) { } MercurialJobRunner::MercurialJobRunner() : keepRunning(true) { plugin = MercurialPlugin::instance(); connect(this, SIGNAL(error(const QByteArray &)), plugin->outputPane(), SLOT(append(const QByteArray &))); connect(this, SIGNAL(info(const QString &)), plugin->outputPane(), SLOT(append(const QString &))); } MercurialJobRunner::~MercurialJobRunner() { stop(); } void MercurialJobRunner::stop() { mutex.lock(); keepRunning = false; //Create a dummy task to break the cycle QSharedPointer<HgTask> job(0); jobs.enqueue(job); waiter.wakeAll(); mutex.unlock(); wait(); } void MercurialJobRunner::restart() { stop(); mutex.lock(); keepRunning = true; mutex.unlock(); start(); } void MercurialJobRunner::getSettings() { MercurialSettings *settings = MercurialPlugin::instance()->settings(); binary = settings->binary(); timeout = settings->timeout(); standardArguments = settings->standardArguments(); } void MercurialJobRunner::enqueueJob(QSharedPointer<HgTask> &job) { mutex.lock(); jobs.enqueue(job); waiter.wakeAll(); mutex.unlock(); } void MercurialJobRunner::run() { getSettings(); forever { mutex.lock(); while (jobs.count() == 0) waiter.wait(&mutex); if (!keepRunning) { jobs.clear(); mutex.unlock(); return; } QSharedPointer<HgTask> job = jobs.dequeue(); mutex.unlock(); task(job); } } void MercurialJobRunner::task(QSharedPointer<HgTask> &job) { HgTask *taskData = job.data(); if (taskData->shouldEmit()) //Call the job's signal so the Initator of the job can process the data //Because the QSharedPointer that holds the HgTask will go out of scope and hence be deleted //we have to block and wait until the signal is delivered connect(this, SIGNAL(output(const QByteArray&)), taskData, SIGNAL(rawData(const QByteArray&)), Qt::BlockingQueuedConnection); else if (taskData->displayEditor()) //An editor has been created to display the data so send it there connect(this, SIGNAL(output(const QByteArray&)), taskData->displayEditor(), SLOT(setPlainTextData(const QByteArray&))); else //Just output the data to the Mercurial output window connect(this, SIGNAL(output(const QByteArray &)), plugin->outputPane(), SLOT(append(const QByteArray &))); QString time = QTime::currentTime().toString(QLatin1String("HH:mm")); QString starting = tr("%1 Calling: %2 %3\n").arg(time, "hg", taskData->args().join(" ")); //infom the user of what we are going to try and perform emit info(starting); if (Constants::debug) qDebug() << Q_FUNC_INFO << "Repository root is " << taskData->repositoryRoot(); QProcess hgProcess; hgProcess.setWorkingDirectory(taskData->repositoryRoot()); QStringList args = standardArguments; args << taskData->args(); hgProcess.start(binary, args); if (!hgProcess.waitForStarted()) { QByteArray errorArray(Constants::ERRORSTARTING); emit error(errorArray); return; } hgProcess.closeWriteChannel(); if (!hgProcess.waitForFinished(timeout)) { hgProcess.terminate(); QByteArray errorArray(Constants::TIMEDOUT); emit error(errorArray); return; } if ((hgProcess.exitStatus() == QProcess::NormalExit) && (hgProcess.exitCode() == 0)) { QByteArray stdout = hgProcess.readAllStandardOutput(); /* * sometimes success means output is actually on error channel (stderr) * e.g. "hg revert" outputs "no changes needed to 'file'" on stderr if file has not changed * from revision specified */ if (stdout == "") stdout = hgProcess.readAllStandardError(); emit output(stdout); } else { QByteArray stderr = hgProcess.readAllStandardError(); emit error(stderr); } hgProcess.close(); //the signal connection is to last only for the duration of a job/task. next time a new //output signal connection must be made disconnect(this, SIGNAL(output(const QByteArray &)), 0, 0); }