diff --git a/src/core/jobs/transactionsequence.cpp b/src/core/jobs/transactionsequence.cpp index a7b50f075..89b956d5f 100644 --- a/src/core/jobs/transactionsequence.cpp +++ b/src/core/jobs/transactionsequence.cpp @@ -1,243 +1,241 @@ /* Copyright (c) 2006-2008 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "transactionsequence.h" #include "transactionjobs.h" #include "job_p.h" #include using namespace Akonadi; class Akonadi::TransactionSequencePrivate : public JobPrivate { public: TransactionSequencePrivate(TransactionSequence *parent) : JobPrivate(parent) , mState(Idle) { } enum TransactionState { Idle, Running, WaitingForSubjobs, RollingBack, Committing }; Q_DECLARE_PUBLIC(TransactionSequence) TransactionState mState; QSet mIgnoredErrorJobs; bool mAutoCommit = true; void commitResult(KJob *job) { Q_Q(TransactionSequence); if (job->error()) { q->setError(job->error()); q->setErrorText(job->errorText()); } q->emitResult(); } void rollbackResult(KJob *job) { Q_Q(TransactionSequence); Q_UNUSED(job); q->emitResult(); } QString jobDebuggingString() const override; }; QString Akonadi::TransactionSequencePrivate::jobDebuggingString() const { //TODO add state return QStringLiteral("autocommit %1").arg(mAutoCommit); } TransactionSequence::TransactionSequence(QObject *parent) : Job(new TransactionSequencePrivate(this), parent) { } TransactionSequence::~TransactionSequence() { } bool TransactionSequence::addSubjob(KJob *job) { Q_D(TransactionSequence); //Don't abort the rollback job, while keeping the state set. if (d->mState == TransactionSequencePrivate::RollingBack) { return Job::addSubjob(job); } if (error()) { //This can happen if a rollback is in progress, so make sure we don't set the state back to running. job->kill(EmitResult); return false; } // TODO KDE5: remove property hack once SpecialCollectionsRequestJob has been fixed if (d->mState == TransactionSequencePrivate::Idle && !property("transactionsDisabled").toBool()) { d->mState = TransactionSequencePrivate::Running; // needs to be set before creating the transaction job to avoid infinite recursion new TransactionBeginJob(this); } else { d->mState = TransactionSequencePrivate::Running; } return Job::addSubjob(job); } void TransactionSequence::slotResult(KJob *job) { Q_D(TransactionSequence); if (!job->error() || d->mIgnoredErrorJobs.contains(job)) { // If we have an error but want to ignore it, we can't call Job::slotResult // because it would confuse the subjob queue processing logic. Just removing // the subjob instead is fine. if (!job->error()) { Job::slotResult(job); } else { Job::removeSubjob(job); } if (!hasSubjobs()) { if (d->mState == TransactionSequencePrivate::WaitingForSubjobs) { if (property("transactionsDisabled").toBool()) { emitResult(); return; } d->mState = TransactionSequencePrivate::Committing; TransactionCommitJob *job = new TransactionCommitJob(this); connect(job, &TransactionCommitJob::result, [d](KJob *job) { d->commitResult(job);}); - } else if (d->mState == TransactionSequencePrivate::RollingBack) { - emitResult(); } } } else { setError(job->error()); setErrorText(job->errorText()); removeSubjob(job); // cancel all subjobs in case someone else is listening (such as ItemSync), but without notifying ourselves again foreach (KJob *job, subjobs()) { disconnect(job, &KJob::result, this, &TransactionSequence::slotResult); job->kill(EmitResult); } clearSubjobs(); if (d->mState == TransactionSequencePrivate::Running || d->mState == TransactionSequencePrivate::WaitingForSubjobs) { if (property("transactionsDisabled").toBool()) { emitResult(); return; } d->mState = TransactionSequencePrivate::RollingBack; TransactionRollbackJob *job = new TransactionRollbackJob(this); connect(job, &TransactionRollbackJob::result, this, [d](KJob *job) { d->rollbackResult(job);}); } } } void TransactionSequence::commit() { Q_D(TransactionSequence); if (d->mState == TransactionSequencePrivate::Running) { d->mState = TransactionSequencePrivate::WaitingForSubjobs; } else { // we never got any subjobs, that means we never started a transaction // so we can just quit here if (d->mState == TransactionSequencePrivate::Idle) { emitResult(); } return; } if (subjobs().isEmpty()) { if (property("transactionsDisabled").toBool()) { emitResult(); return; } if (!error()) { d->mState = TransactionSequencePrivate::Committing; TransactionCommitJob *job = new TransactionCommitJob(this); connect(job, &TransactionCommitJob::result, this, [d](KJob *job) { d->commitResult(job);}); } else { d->mState = TransactionSequencePrivate::RollingBack; TransactionRollbackJob *job = new TransactionRollbackJob(this); connect(job, &TransactionRollbackJob::result, this, [d](KJob *job) { d->rollbackResult(job);}); } } } void TransactionSequence::setIgnoreJobFailure(KJob *job) { Q_D(TransactionSequence); // make sure this is one of our sub jobs Q_ASSERT(subjobs().contains(job)); d->mIgnoredErrorJobs.insert(job); } void TransactionSequence::doStart() { Q_D(TransactionSequence); if (d->mAutoCommit) { if (d->mState == TransactionSequencePrivate::Idle) { emitResult(); } else { commit(); } } } void TransactionSequence::setAutomaticCommittingEnabled(bool enable) { Q_D(TransactionSequence); d->mAutoCommit = enable; } void TransactionSequence::rollback() { Q_D(TransactionSequence); setError(UserCanceled); // we never really started if (d->mState == TransactionSequencePrivate::Idle) { emitResult(); return; } // TODO cancel not yet executed sub-jobs here d->mState = TransactionSequencePrivate::RollingBack; TransactionRollbackJob *job = new TransactionRollbackJob(this); connect(job, &TransactionRollbackJob::result, this, [d](KJob *job) { d->rollbackResult(job);}); } #include "moc_transactionsequence.cpp"