File indexing completed on 2024-05-12 05:12:45
0001 /* 0002 This file is part of Akonadi. 0003 0004 SPDX-FileCopyrightText: 2009 KDAB 0005 SPDX-FileContributor: Till Adam <adam@kde.org> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include "jobtracker.h" 0011 #include "akonadiconsole_debug.h" 0012 #include "jobtrackeradaptor.h" 0013 #include <KLocalizedString> 0014 #include <QString> 0015 #include <QStringList> 0016 #include <akonadi/private/instance_p.h> 0017 0018 #include <cassert> 0019 #include <chrono> 0020 0021 using namespace std::chrono_literals; 0022 QString JobInfo::stateAsString() const 0023 { 0024 switch (state) { 0025 case Initial: 0026 return i18n("Waiting"); 0027 case Running: 0028 return i18n("Running"); 0029 case Ended: 0030 return i18n("Ended"); 0031 case Failed: 0032 return i18n("Failed: %1", error); 0033 default: 0034 return i18n("Unknown state!"); 0035 } 0036 } 0037 0038 class JobTrackerPrivate 0039 { 0040 public: 0041 explicit JobTrackerPrivate(JobTracker *_q) 0042 : lastId(42) 0043 , timer(_q) 0044 , disabled(false) 0045 , q(_q) 0046 { 0047 timer.setSingleShot(true); 0048 timer.setInterval(200ms); 0049 QObject::connect(&timer, &QTimer::timeout, q, &JobTracker::signalUpdates); 0050 } 0051 0052 [[nodiscard]] bool isSession(int id) const 0053 { 0054 return id < -1; 0055 } 0056 0057 void startUpdatedSignalTimer() 0058 { 0059 if (!timer.isActive() && !disabled) { 0060 timer.start(); 0061 } 0062 } 0063 0064 QStringList sessions; 0065 QHash<QString, int> nameToId; 0066 QHash<int, QList<int>> childJobs; 0067 QHash<int, JobInfo> infoList; 0068 int lastId; 0069 QTimer timer; 0070 bool disabled; 0071 QList<QPair<int, int>> unpublishedUpdates; 0072 0073 private: 0074 JobTracker *const q; 0075 }; 0076 0077 JobTracker::JobTracker(const char *name, QObject *parent) 0078 : QObject(parent) 0079 , d(new JobTrackerPrivate(this)) 0080 { 0081 new JobTrackerAdaptor(this); 0082 const QString suffix = Akonadi::Instance::identifier().isEmpty() ? QString() : QLatin1Char('-') + Akonadi::Instance::identifier(); 0083 QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.akonadiconsole") + suffix); 0084 QDBusConnection::sessionBus().registerObject(QLatin1Char('/') + QLatin1StringView(name), this, QDBusConnection::ExportAdaptors); 0085 } 0086 0087 JobTracker::~JobTracker() = default; 0088 0089 void JobTracker::jobCreated(const QString &session, const QString &jobName, const QString &parent, const QString &jobType, const QString &debugString) 0090 { 0091 if (d->disabled || session.isEmpty() || jobName.isEmpty()) { 0092 return; 0093 } 0094 0095 int parentId = parent.isEmpty() ? -1 /*for now*/ : idForJob(parent); 0096 0097 if (!parent.isEmpty() && parentId == -1) { 0098 qCWarning(AKONADICONSOLE_LOG) << "JobTracker: Job" << jobName << "arrived before its parent" << parent << " jobType=" << jobType 0099 << "! Fix the library!"; 0100 jobCreated(session, parent, QString(), QStringLiteral("dummy job type"), QString()); 0101 parentId = idForJob(parent); 0102 assert(parentId != -1); 0103 } 0104 int sessionId = idForSession(session); 0105 // check if it's a new session, if so, add it 0106 if (sessionId == -1) { 0107 Q_EMIT aboutToAdd(d->sessions.count(), -1); 0108 d->sessions.append(session); 0109 Q_EMIT added(); 0110 sessionId = idForSession(session); 0111 } 0112 if (parent.isEmpty()) { 0113 parentId = sessionId; 0114 } 0115 0116 // deal with the job 0117 const int existingId = idForJob(jobName); 0118 if (existingId != -1) { 0119 if (d->infoList.value(existingId).state == JobInfo::Running) { 0120 qCDebug(AKONADICONSOLE_LOG) << "Job was already known and still running:" << jobName << "from" 0121 << d->infoList.value(existingId).timestamp.secsTo(QDateTime::currentDateTime()) << "s ago"; 0122 } 0123 // otherwise it just means the pointer got reused... insert duplicate 0124 } 0125 0126 assert(parentId != -1); 0127 QList<int> &kids = d->childJobs[parentId]; 0128 const int pos = kids.size(); 0129 0130 Q_EMIT aboutToAdd(pos, parentId); 0131 0132 const int id = d->lastId++; 0133 0134 JobInfo info; 0135 info.name = jobName; 0136 info.parent = parentId; 0137 info.state = JobInfo::Initial; 0138 info.timestamp = QDateTime::currentDateTime(); 0139 info.type = jobType; 0140 info.debugString = debugString; 0141 d->infoList.insert(id, info); 0142 d->nameToId.insert(jobName, id); // this replaces any previous entry for jobName, which is exactly what we want 0143 kids << id; 0144 0145 Q_EMIT added(); 0146 } 0147 0148 void JobTracker::jobEnded(const QString &jobName, const QString &error) 0149 { 0150 if (d->disabled) { 0151 return; 0152 } 0153 // this is called from dbus, so better be defensive 0154 const int jobId = idForJob(jobName); 0155 if (jobId == -1 || !d->infoList.contains(jobId)) { 0156 return; 0157 } 0158 0159 JobInfo &info = d->infoList[jobId]; 0160 if (error.isEmpty()) { 0161 info.state = JobInfo::Ended; 0162 } else { 0163 info.state = JobInfo::Failed; 0164 info.error = error; 0165 } 0166 info.endedTimestamp = QDateTime::currentDateTime(); 0167 0168 d->unpublishedUpdates << QPair<int, int>(d->childJobs[info.parent].size() - 1, info.parent); 0169 d->startUpdatedSignalTimer(); 0170 } 0171 0172 void JobTracker::jobStarted(const QString &jobName) 0173 { 0174 if (d->disabled) { 0175 return; 0176 } 0177 // this is called from dbus, so better be defensive 0178 const int jobId = idForJob(jobName); 0179 if (jobId == -1 || !d->infoList.contains(jobId)) { 0180 return; 0181 } 0182 0183 JobInfo &info = d->infoList[jobId]; 0184 info.state = JobInfo::Running; 0185 info.startedTimestamp = QDateTime::currentDateTime(); 0186 0187 d->unpublishedUpdates << QPair<int, int>(d->childJobs[info.parent].size() - 1, info.parent); 0188 d->startUpdatedSignalTimer(); 0189 } 0190 0191 QStringList JobTracker::sessions() const 0192 { 0193 return d->sessions; 0194 } 0195 0196 QList<int> JobTracker::childJobs(int parentId) const 0197 { 0198 return d->childJobs.value(parentId); 0199 } 0200 0201 int JobTracker::jobCount(int parentId) const 0202 { 0203 return d->childJobs.value(parentId).count(); 0204 } 0205 0206 int JobTracker::jobIdAt(int childPos, int parentId) const 0207 { 0208 return d->childJobs.value(parentId).at(childPos); 0209 } 0210 0211 // only works on jobs 0212 int JobTracker::idForJob(const QString &job) const 0213 { 0214 return d->nameToId.value(job, -1); 0215 } 0216 0217 // To find a session, we take the offset in the list of sessions 0218 // in order of appearance, add one, and make it negative. That 0219 // way we can discern session ids from job ids and use -1 for invalid 0220 int JobTracker::idForSession(const QString &session) const 0221 { 0222 return (d->sessions.indexOf(session) + 2) * -1; 0223 } 0224 0225 QString JobTracker::sessionForId(int _id) const 0226 { 0227 const int id = (-_id) - 2; 0228 assert(d->sessions.size() > id); 0229 if (!d->sessions.isEmpty()) { 0230 return d->sessions.at(id); 0231 } else { 0232 return {}; 0233 } 0234 } 0235 0236 int JobTracker::parentId(int id) const 0237 { 0238 if (d->isSession(id)) { 0239 return -1; 0240 } else { 0241 return d->infoList.value(id).parent; 0242 } 0243 } 0244 0245 int JobTracker::rowForJob(int id, int parentId) const 0246 { 0247 const QList<int> children = childJobs(parentId); 0248 // Simple version: 0249 // return children.indexOf(id); 0250 // But we can do faster since the vector is sorted 0251 return std::lower_bound(children.constBegin(), children.constEnd(), id) - children.constBegin(); 0252 } 0253 0254 JobInfo JobTracker::info(int id) const 0255 { 0256 assert(d->infoList.contains(id)); 0257 return d->infoList.value(id); 0258 } 0259 0260 void JobTracker::clear() 0261 { 0262 d->sessions.clear(); 0263 d->nameToId.clear(); 0264 d->childJobs.clear(); 0265 d->infoList.clear(); 0266 d->unpublishedUpdates.clear(); 0267 } 0268 0269 void JobTracker::setEnabled(bool on) 0270 { 0271 d->disabled = !on; 0272 } 0273 0274 bool JobTracker::isEnabled() const 0275 { 0276 return !d->disabled; 0277 } 0278 0279 void JobTracker::signalUpdates() 0280 { 0281 if (!d->unpublishedUpdates.isEmpty()) { 0282 Q_EMIT updated(d->unpublishedUpdates); 0283 d->unpublishedUpdates.clear(); 0284 } 0285 } 0286 0287 #include "moc_jobtracker.cpp"