File indexing completed on 2024-05-19 04:39:57
0001 /* 0002 This file is part of the KDE project 0003 0004 SPDX-FileCopyrightText: 2007-2008 Hamish Rodda <rodda@kde.org> 0005 SPDX-FileCopyrightText: 2023 Igor Kushnir <igorkuo@gmail.com> 0006 0007 SPDX-License-Identifier: LGPL-2.0-or-later 0008 */ 0009 0010 #include "ksequentialcompoundjob.h" 0011 #include "ksequentialcompoundjob_p.h" 0012 0013 #include "debug.h" 0014 0015 using namespace KDevCoreAddons; 0016 0017 KSequentialCompoundJobPrivate::KSequentialCompoundJobPrivate() = default; 0018 KSequentialCompoundJobPrivate::~KSequentialCompoundJobPrivate() = default; 0019 0020 bool KSequentialCompoundJobPrivate::isCurrentlyRunningSubjob(KJob *job) const 0021 { 0022 return m_jobIndex >= 0 && !m_subjobs.empty() && job == m_subjobs.constFirst(); 0023 } 0024 0025 void KSequentialCompoundJobPrivate::startNextSubjob() 0026 { 0027 ++m_jobIndex; 0028 Q_ASSERT(!m_subjobs.empty()); 0029 auto *const job = m_subjobs.front(); 0030 0031 qCDebug(UTIL) << "starting subjob" << m_jobIndex + 1 << "of" << m_jobCount << ':' << job; 0032 job->start(); 0033 } 0034 0035 void KSequentialCompoundJobPrivate::disconnectSubjob(KJob *job) 0036 { 0037 Q_Q(KSequentialCompoundJob); 0038 QObject::disconnect(job, &KJob::percentChanged, q, &KSequentialCompoundJob::subjobPercentChanged); 0039 KCompoundJobPrivate::disconnectSubjob(job); 0040 } 0041 0042 KSequentialCompoundJob::KSequentialCompoundJob(QObject *parent) 0043 : KSequentialCompoundJob(*new KSequentialCompoundJobPrivate, parent) 0044 { 0045 } 0046 0047 KSequentialCompoundJob::KSequentialCompoundJob(KSequentialCompoundJobPrivate &dd, QObject *parent) 0048 : KCompoundJob(dd, parent) 0049 { 0050 setCapabilities(Killable); 0051 } 0052 0053 KSequentialCompoundJob::~KSequentialCompoundJob() = default; 0054 0055 void KSequentialCompoundJob::setAbortOnSubjobError(bool abort) 0056 { 0057 Q_D(KSequentialCompoundJob); 0058 d->m_abortOnSubjobError = abort; 0059 } 0060 0061 void KSequentialCompoundJob::start() 0062 { 0063 Q_D(KSequentialCompoundJob); 0064 if (d->m_subjobs.empty()) { 0065 qCDebug(UTIL) << "no subjobs, finishing in start()"; 0066 emitResult(); 0067 return; 0068 } 0069 0070 d->startNextSubjob(); 0071 } 0072 0073 void KSequentialCompoundJob::subjobPercentChanged(KJob *job, unsigned long percent) 0074 { 0075 Q_D(KSequentialCompoundJob); 0076 Q_ASSERT(d->m_jobIndex < d->m_jobCount); // invariant 0077 if (!d->isCurrentlyRunningSubjob(job)) { 0078 qCDebug(UTIL) << "ignoring percentChanged() signal emitted by an unstarted or finished subjob" << job; 0079 return; 0080 } 0081 Q_ASSERT(d->m_jobIndex >= 0); 0082 0083 const unsigned long totalPercent = (100.0 * d->m_jobIndex + percent) / d->m_jobCount; 0084 qCDebug(UTIL) << "subjob percent:" << percent << "; total percent:" << totalPercent; 0085 setPercent(totalPercent); 0086 } 0087 0088 void KSequentialCompoundJob::subjobFinished(KJob *job) 0089 { 0090 Q_D(KSequentialCompoundJob); 0091 if (d->m_killingSubjob || isFinished()) { 0092 // doKill() will return true and this compound job will finish, or already finished 0093 removeSubjob(job); 0094 return; 0095 } 0096 0097 Q_ASSERT(d->m_jobIndex < d->m_jobCount); // invariant 0098 // Note: isCurrentlyRunningSubjob(job) must be checked before calling removeSubjob(job). 0099 if (!d->isCurrentlyRunningSubjob(job)) { 0100 qCDebug(UTIL) << "unstarted subjob finished:" << job; 0101 removeSubjob(job); 0102 return; 0103 } 0104 0105 const bool registeredSubjob = removeSubjob(job); 0106 Q_ASSERT(registeredSubjob); // because isCurrentlyRunningSubjob(job) returned true 0107 0108 Q_ASSERT(d->m_jobIndex >= 0); // because isCurrentlyRunningSubjob(job) returned true 0109 const unsigned long totalPercent = 100.0 * (d->m_jobIndex + 1) / d->m_jobCount; 0110 qCDebug(UTIL) << "subjob finished:" << job << "; total percent:" << totalPercent; 0111 setPercent(totalPercent); 0112 0113 int error = job->error(); 0114 if (!error && d->m_killingFailed) { 0115 error = KilledJobError; 0116 } 0117 0118 // Abort if job is the subjob we failed to kill and in case of error. 0119 const bool abort = d->m_killingFailed || (d->m_abortOnSubjobError && error); 0120 if (abort) { 0121 qCDebug(UTIL) << "aborting on subjob error:" << error << job->errorText(); 0122 } 0123 0124 // Finish in order to abort, or if all subjobs have finished. Propagate the last-run subjob's error. 0125 if (abort || d->m_subjobs.empty()) { 0126 setError(error); 0127 setErrorText(job->errorText()); 0128 emitResult(); 0129 return; 0130 } 0131 0132 qCDebug(UTIL) << "remaining subjobs:" << d->m_subjobs; 0133 d->startNextSubjob(); 0134 } 0135 0136 bool KSequentialCompoundJob::addSubjob(KJob *job) 0137 { 0138 Q_D(KSequentialCompoundJob); 0139 if (!KCompoundJob::addSubjob(job)) { 0140 return false; 0141 } 0142 ++d->m_jobCount; 0143 connect(job, &KJob::percentChanged, this, &KSequentialCompoundJob::subjobPercentChanged); 0144 return true; 0145 } 0146 0147 bool KSequentialCompoundJob::doKill() 0148 { 0149 Q_D(KSequentialCompoundJob); 0150 // Don't check isFinished() here, because KJob::kill() calls doKill() only if the job has not finished. 0151 if (d->m_killingSubjob) { 0152 qCDebug(UTIL) << "killing sequential compound job recursively fails"; 0153 return false; 0154 } 0155 if (d->m_jobIndex == -1) { 0156 qCDebug(UTIL) << "killing unstarted sequential compound job"; 0157 // Any unstarted subjobs will be deleted along with this compound job, which is their parent. 0158 return true; 0159 } 0160 if (d->m_subjobs.empty()) { 0161 qCDebug(UTIL) << "killing sequential compound job with zero remaining subjobs"; 0162 return true; 0163 } 0164 0165 auto *const job = d->m_subjobs.front(); 0166 qCDebug(UTIL) << "killing running subjob" << job; 0167 0168 d->m_killingSubjob = true; 0169 const bool killed = job->kill(); 0170 d->m_killingSubjob = false; 0171 0172 d->m_killingFailed = !killed; 0173 if (d->m_killingFailed) { 0174 qCDebug(UTIL) << "failed to kill subjob" << job; 0175 if (d->m_subjobs.empty() || d->m_subjobs.constFirst() != job) { 0176 qCDebug(UTIL) << "... but the subjob finished or was removed, assume killed. Remaining subjobs:" << d->m_subjobs; 0177 return true; 0178 } 0179 } 0180 0181 return killed; 0182 } 0183 0184 #include "moc_ksequentialcompoundjob.cpp" 0185 #include "moc_ksimplesequentialcompoundjob.cpp"