# # # patch "src/monotone/MonotoneManager.cpp" # from [9ad384c8bd306c2eb6414627dee5b41c2dc50966] # to [d0a027af9e6137b3be7483b63db71e94b0af8b0d] # # patch "src/monotone/MonotoneThread.cpp" # from [7a52cb70225dfe2926657f89a35b7402d315016d] # to [fde2557e39098eb7ea87b7b84fb529be715b3ee8] # ============================================================ --- src/monotone/MonotoneManager.cpp 9ad384c8bd306c2eb6414627dee5b41c2dc50966 +++ src/monotone/MonotoneManager.cpp d0a027af9e6137b3be7483b63db71e94b0af8b0d @@ -19,10 +19,14 @@ ***************************************************************************/ #include "MonotoneManager.h" +#include "MonotoneUtil.h" #include "vocab.h" #include "BasicIOParser.h" #include "SignalWaiter.h" +#include +#include + const QString MonotoneManager::MinInterfaceVersion = MIN_MTN_INT_VERSION; const QString MonotoneManager::MaxInterfaceVersion = MAX_MTN_INT_VERSION; @@ -68,6 +72,26 @@ MonotoneThread * MonotoneManager::getThr MonotoneThread * MonotoneManager::getThreadForDatabase(const DatabaseFile & database) { + QFile dbfile(database); + if (!dbfile.open(QIODevice::ReadOnly)) + { + throw GuitoneException(tr("Could not open database for reading")); + } + + if (dbfile.read(15) != QByteArray("SQLite format 3")) + { + dbfile.close(); + throw GuitoneException(tr("Database file not an SQLite 3 database")); + } + + if (!dbfile.seek(60) || dbfile.read(4) != QByteArray("_MTN")) + { + dbfile.close(); + throw GuitoneException(tr("SQLite database is not a monotone " + "database or a database of an older, " + "unsupported version of monotone")); + } + dbfile.close(); return getThread(database, QString()); } @@ -97,6 +121,13 @@ MonotoneThread * MonotoneManager::getThr identMap.insert(threadNumber, ident); threadIDs.append(threadNumber); + static bool processErrorRegisteredMetaType = false; + if (!processErrorRegisteredMetaType) + { + qRegisterMetaType("QProcess::ProcessError"); + processErrorRegisteredMetaType = true; + } + connect( thread, SIGNAL(aborted(int, QProcess::ProcessError, const QString &)), this, SLOT(aborted(int, QProcess::ProcessError, const QString &)) @@ -195,7 +226,6 @@ DatabaseFile MonotoneManager::getDatabas return databaseFilePath; } -//! \todo many-to-one resolution void MonotoneManager::aborted(int threadNumber, QProcess::ProcessError error, const QString & message) { QMutexLocker locker(&lock); @@ -204,7 +234,8 @@ void MonotoneManager::aborted(int thread MonotoneThread * thread = threadMap.value(threadNumber); - // FIXME: do we want to re-start the process right here? + QString databaseFile = thread->getDatabaseFilePath(); + QString workspacePath = thread->getWorkspacePath(); disconnect( thread, SIGNAL(aborted(int, QProcess::ProcessError, const QString &)), @@ -216,8 +247,60 @@ void MonotoneManager::aborted(int thread delete thread; - W(QString("thread %1 aborted (error %2: %3)") - .arg(threadNumber).arg(error).arg(message)); + // + // the thread has been removed cleanly, now report what was going on + // + + QString mtnError = MonotoneUtil::stripMtnPrefix(message); + QString processErrorTranslated; + + switch (error) + { + case QProcess::FailedToStart: + processErrorTranslated = tr("failed to start"); + break; + case QProcess::Crashed: + processErrorTranslated = tr("crashed"); + break; + case QProcess::Timedout: + processErrorTranslated = tr("timed out waiting for data"); + break; + case QProcess::WriteError: + processErrorTranslated = tr("failed to read"); + break; + case QProcess::ReadError: + processErrorTranslated = tr("failed to write"); + break; + case QProcess::UnknownError: + processErrorTranslated = tr("unknown error"); + break; + default: I(false); + } + + // log something reasonable + C(QString("thread %1 died (%2) - stderr was %3") + .arg(threadNumber).arg(processErrorTranslated).arg(mtnError)); + + // display something understandable to the user + QString userMessage(tr( + "The monotone process for '%1' died (%2). " + "If you think this is a bug in guitone, " + "please report it to the author!" + ).arg(workspacePath.isEmpty() ? databaseFile : workspacePath) + .arg(processErrorTranslated) + ); + + if (!mtnError.isEmpty()) + { + userMessage.append(tr("\n\nLast error output was:\n%1").arg(mtnError)); + } + + QMessageBox::critical( + NULL, + tr("Error"), + userMessage, + QMessageBox::Ok, 0, 0 + ); } bool MonotoneManager::singleRun(const QString & mtnBinary, ============================================================ --- src/monotone/MonotoneThread.cpp 7a52cb70225dfe2926657f89a35b7402d315016d +++ src/monotone/MonotoneThread.cpp fde2557e39098eb7ea87b7b84fb529be715b3ee8 @@ -181,7 +181,25 @@ void MonotoneThread::run() return; } - D(QString("thread %1: process started").arg(threadNumber)); + // waitForReadyRead returns false, if there are no data available + // (stdio shouldn't return anything on either channel if everything is ok) + // or the time is up + // FIXME: find the proper timeout for our use case here or better an + // alternative way to detect if something went wrong while setting up + // the process _before_ we send the whole thread to sleep later on + // waiting silently for his first task... + process->setReadChannel(QProcess::StandardError); + if (process->waitForReadyRead(1000)) + { + QString err = QString::fromUtf8(process->readAllStandardError()); + + emit aborted(threadNumber, process->error(), err); + cleanup(process); + return; + } + process->setReadChannel(QProcess::StandardOutput); + + L(QString("thread %1: process started").arg(threadNumber)); emit started(threadNumber); QTextStream streamProcess(process); @@ -192,6 +210,12 @@ void MonotoneThread::run() while (!doAbort) { + // send the thread to sleep if there are no tasks to process + lock.lock(); + if (queue.size() == 0) + waitForTasks.wait(&lock); + lock.unlock(); + if (process->state() != QProcess::Running) { emit aborted(threadNumber, process->error(), @@ -201,12 +225,6 @@ void MonotoneThread::run() return; } - // send the thread to sleep if there are no tasks to process - lock.lock(); - if (queue.size() == 0) - waitForTasks.wait(&lock); - lock.unlock(); - if (!processingTask) { MonotoneTask task = queue.head(); @@ -215,7 +233,8 @@ void MonotoneThread::run() processingTask = true; } - if (!process->waitForReadyRead(-1)) + // FIXME: make this configurable! + if (!process->waitForReadyRead(10000)) { emit aborted(threadNumber, process->error(), QString::fromUtf8(process->readAllStandardError())