Warning, file /pim/mailcommon/src/job/jobscheduler.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /**
0002  * SPDX-FileCopyrightText: 2004 David Faure <faure@kde.org>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "jobscheduler.h"
0008 using namespace MailCommon;
0009 
0010 ScheduledTask::ScheduledTask(const Akonadi::Collection &folder, bool immediate)
0011     : mCurrentFolder(folder)
0012     , mImmediate(immediate)
0013 {
0014 }
0015 
0016 Akonadi::Collection ScheduledTask::folder() const
0017 {
0018     return mCurrentFolder;
0019 }
0020 
0021 bool ScheduledTask::isImmediate() const
0022 {
0023     return mImmediate;
0024 }
0025 
0026 ScheduledTask::~ScheduledTask() = default;
0027 
0028 JobScheduler::JobScheduler(QObject *parent)
0029     : QObject(parent)
0030     , mTimer(this)
0031 {
0032     connect(&mTimer, &QTimer::timeout, this, &JobScheduler::slotRunNextJob);
0033     // No need to start the internal timer yet, we wait for a task to be scheduled
0034 }
0035 
0036 JobScheduler::~JobScheduler()
0037 {
0038     qDeleteAll(mTaskList);
0039     mTaskList.clear();
0040     delete mCurrentTask;
0041     mCurrentTask = nullptr;
0042     delete mCurrentJob;
0043 }
0044 
0045 void JobScheduler::registerTask(ScheduledTask *task)
0046 {
0047     bool immediate = task->isImmediate();
0048     int typeId = task->taskTypeId();
0049     if (typeId) {
0050         const Akonadi::Collection folder = task->folder();
0051         // Search for an identical task already scheduled
0052         TaskList::Iterator end(mTaskList.end());
0053         for (TaskList::Iterator it = mTaskList.begin(); it != end; ++it) {
0054             if ((*it)->taskTypeId() == typeId && (*it)->folder() == folder) {
0055 #ifdef DEBUG_SCHEDULER
0056                 qCDebug(MAILCOMMON_LOG) << "JobScheduler: already having task type" << typeId << "for folder" << folder->label();
0057 #endif
0058                 delete task;
0059                 if (!mCurrentTask && immediate) {
0060                     ScheduledTask *task = *it;
0061                     removeTask(it);
0062                     runTaskNow(task);
0063                 }
0064                 return;
0065             }
0066         }
0067         // Note that scheduling an identical task as the one currently running is allowed.
0068     }
0069     if (!mCurrentTask && immediate) {
0070         runTaskNow(task);
0071     } else {
0072 #ifdef DEBUG_SCHEDULER
0073         qCDebug(MAILCOMMON_LOG) << "JobScheduler: adding task" << task << "(type" << task->taskTypeId() << ") for folder" << task->folder()
0074                                 << task->folder().name();
0075 #endif
0076         mTaskList.append(task);
0077         if (immediate) {
0078             ++mPendingImmediateTasks;
0079         }
0080         if (!mCurrentTask && !mTimer.isActive()) {
0081             restartTimer();
0082         }
0083     }
0084 }
0085 
0086 void JobScheduler::removeTask(TaskList::Iterator &it)
0087 {
0088     if ((*it)->isImmediate()) {
0089         --mPendingImmediateTasks;
0090     }
0091     mTaskList.erase(it);
0092 }
0093 
0094 void JobScheduler::interruptCurrentTask()
0095 {
0096     Q_ASSERT(mCurrentTask);
0097 #ifdef DEBUG_SCHEDULER
0098     qCDebug(MAILCOMMON_LOG) << "JobScheduler: interrupting job" << mCurrentJob << "for folder" << mCurrentTask->folder()->label();
0099 #endif
0100     // File it again. This will either delete it or put it in mTaskList.
0101     registerTask(mCurrentTask);
0102     mCurrentTask = nullptr;
0103     mCurrentJob->kill(); // This deletes the job and calls slotJobFinished!
0104 }
0105 
0106 void JobScheduler::slotRunNextJob()
0107 {
0108     while (!mCurrentJob) {
0109 #ifdef DEBUG_SCHEDULER
0110         qCDebug(MAILCOMMON_LOG) << "JobScheduler: slotRunNextJob";
0111 #endif
0112         Q_ASSERT(mCurrentTask == nullptr);
0113         ScheduledTask *task = nullptr;
0114         // Find a task suitable for being run
0115         TaskList::Iterator end(mTaskList.end());
0116         for (TaskList::Iterator it = mTaskList.begin(); it != end; ++it) {
0117             // Remove if folder died
0118             const Akonadi::Collection folder = (*it)->folder();
0119             if (!folder.isValid()) {
0120 #ifdef DEBUG_SCHEDULER
0121                 qCDebug(MAILCOMMON_LOG) << "  folder for task" << (*it) << "was deleted";
0122 #endif
0123                 removeTask(it);
0124                 if (!mTaskList.isEmpty()) {
0125                     slotRunNextJob(); // to avoid the mess with invalid iterators :)
0126                 } else {
0127                     mTimer.stop();
0128                 }
0129                 return;
0130             }
0131 #ifdef DEBUG_SCHEDULER
0132             qCDebug(MAILCOMMON_LOG) << "  looking at folder" << folder.name();
0133 #endif
0134             task = *it;
0135             removeTask(it);
0136             break;
0137         }
0138 
0139         if (!task) { // found nothing to run, i.e. folder was opened
0140             return; // Timer keeps running, i.e. try again in 1 minute
0141         }
0142 
0143         runTaskNow(task);
0144     } // If nothing to do for that task, loop and find another one to run
0145 }
0146 
0147 void JobScheduler::restartTimer()
0148 {
0149     if (mPendingImmediateTasks > 0) {
0150         slotRunNextJob();
0151     } else {
0152 #ifdef DEBUG_SCHEDULER
0153         mTimer.start(10000); // 10 seconds
0154 #else
0155         mTimer.start(1 * 60000); // 1 minute
0156 #endif
0157     }
0158 }
0159 
0160 void JobScheduler::runTaskNow(ScheduledTask *task)
0161 {
0162     Q_ASSERT(mCurrentTask == nullptr);
0163     if (mCurrentTask) {
0164         interruptCurrentTask();
0165     }
0166     mCurrentTask = task;
0167     mTimer.stop();
0168     mCurrentJob = mCurrentTask->run();
0169 #ifdef DEBUG_SCHEDULER
0170     qCDebug(MAILCOMMON_LOG) << "JobScheduler: task" << mCurrentTask << "(type" << mCurrentTask->taskTypeId() << ")"
0171                             << "for folder" << mCurrentTask->folder()->label() << "returned job" << mCurrentJob << (mCurrentJob ? mCurrentJob->className() : 0);
0172 #endif
0173     if (!mCurrentJob) { // nothing to do, e.g. folder deleted
0174         delete mCurrentTask;
0175         mCurrentTask = nullptr;
0176         if (!mTaskList.isEmpty()) {
0177             restartTimer();
0178         }
0179         return;
0180     }
0181     // Register the job in the folder. This makes it autodeleted if the folder is deleted.
0182 #if 0
0183     mCurrentTask->folder()->storage()->addJob(mCurrentJob);
0184 #endif
0185     connect(mCurrentJob, &ScheduledJob::finished, this, &JobScheduler::slotJobFinished);
0186     mCurrentJob->start();
0187 }
0188 
0189 void JobScheduler::slotJobFinished()
0190 {
0191     // Do we need to test for mCurrentJob->error()? What do we do then?
0192 #ifdef DEBUG_SCHEDULER
0193     qCDebug(MAILCOMMON_LOG) << "JobScheduler: slotJobFinished";
0194 #endif
0195     delete mCurrentTask;
0196     mCurrentTask = nullptr;
0197     mCurrentJob = nullptr;
0198     if (!mTaskList.isEmpty()) {
0199         restartTimer();
0200     }
0201 }
0202 
0203 // D-Bus call to pause any background jobs
0204 void JobScheduler::pause()
0205 {
0206     mPendingImmediateTasks = 0;
0207     if (mCurrentJob && mCurrentJob->isCancellable()) {
0208         interruptCurrentTask();
0209     }
0210     mTimer.stop();
0211 }
0212 
0213 void JobScheduler::resume()
0214 {
0215     restartTimer();
0216 }
0217 
0218 ////
0219 
0220 ScheduledJob::ScheduledJob(const Akonadi::Collection &folder, bool immediate)
0221     : mImmediate(immediate)
0222 {
0223     mCancellable = true;
0224     mSrcFolder = folder;
0225 }
0226 
0227 ScheduledJob::~ScheduledJob() = default;
0228 
0229 #include "moc_jobscheduler.cpp"