Mailcommon

jobscheduler.cpp
1 /**
2  * SPDX-FileCopyrightText: 2004 David Faure <[email protected]>
3  *
4  * SPDX-License-Identifier: GPL-2.0-or-later
5  */
6 
7 #include "jobscheduler.h"
8 #include "mailcommon_debug.h"
9 using namespace MailCommon;
10 
11 ScheduledTask::ScheduledTask(const Akonadi::Collection &folder, bool immediate)
12  : mCurrentFolder(folder)
13  , mImmediate(immediate)
14 {
15 }
16 
18 {
19  return mCurrentFolder;
20 }
21 
22 bool ScheduledTask::isImmediate() const
23 {
24  return mImmediate;
25 }
26 
27 ScheduledTask::~ScheduledTask() = default;
28 
29 JobScheduler::JobScheduler(QObject *parent)
30  : QObject(parent)
31  , mTimer(this)
32 {
33  connect(&mTimer, &QTimer::timeout, this, &JobScheduler::slotRunNextJob);
34  // No need to start the internal timer yet, we wait for a task to be scheduled
35 }
36 
37 JobScheduler::~JobScheduler()
38 {
39  qDeleteAll(mTaskList);
40  mTaskList.clear();
41  delete mCurrentTask;
42  mCurrentTask = nullptr;
43  delete mCurrentJob;
44 }
45 
47 {
48  bool immediate = task->isImmediate();
49  int typeId = task->taskTypeId();
50  if (typeId) {
51  const Akonadi::Collection folder = task->folder();
52  // Search for an identical task already scheduled
53  TaskList::Iterator end(mTaskList.end());
54  for (TaskList::Iterator it = mTaskList.begin(); it != end; ++it) {
55  if ((*it)->taskTypeId() == typeId && (*it)->folder() == folder) {
56 #ifdef DEBUG_SCHEDULER
57  qCDebug(MAILCOMMON_LOG) << "JobScheduler: already having task type" << typeId << "for folder" << folder->label();
58 #endif
59  delete task;
60  if (!mCurrentTask && immediate) {
61  ScheduledTask *task = *it;
62  removeTask(it);
63  runTaskNow(task);
64  }
65  return;
66  }
67  }
68  // Note that scheduling an identical task as the one currently running is allowed.
69  }
70  if (!mCurrentTask && immediate) {
71  runTaskNow(task);
72  } else {
73 #ifdef DEBUG_SCHEDULER
74  qCDebug(MAILCOMMON_LOG) << "JobScheduler: adding task" << task << "(type" << task->taskTypeId() << ") for folder" << task->folder()
75  << task->folder().name();
76 #endif
77  mTaskList.append(task);
78  if (immediate) {
79  ++mPendingImmediateTasks;
80  }
81  if (!mCurrentTask && !mTimer.isActive()) {
82  restartTimer();
83  }
84  }
85 }
86 
87 void JobScheduler::removeTask(TaskList::Iterator &it)
88 {
89  if ((*it)->isImmediate()) {
90  --mPendingImmediateTasks;
91  }
92  mTaskList.erase(it);
93 }
94 
95 void JobScheduler::interruptCurrentTask()
96 {
97  Q_ASSERT(mCurrentTask);
98 #ifdef DEBUG_SCHEDULER
99  qCDebug(MAILCOMMON_LOG) << "JobScheduler: interrupting job" << mCurrentJob << "for folder" << mCurrentTask->folder()->label();
100 #endif
101  // File it again. This will either delete it or put it in mTaskList.
102  registerTask(mCurrentTask);
103  mCurrentTask = nullptr;
104  mCurrentJob->kill(); // This deletes the job and calls slotJobFinished!
105 }
106 
107 void JobScheduler::slotRunNextJob()
108 {
109  while (!mCurrentJob) {
110 #ifdef DEBUG_SCHEDULER
111  qCDebug(MAILCOMMON_LOG) << "JobScheduler: slotRunNextJob";
112 #endif
113  Q_ASSERT(mCurrentTask == nullptr);
114  ScheduledTask *task = nullptr;
115  // Find a task suitable for being run
116  TaskList::Iterator end(mTaskList.end());
117  for (TaskList::Iterator it = mTaskList.begin(); it != end; ++it) {
118  // Remove if folder died
119  const Akonadi::Collection folder = (*it)->folder();
120  if (!folder.isValid()) {
121 #ifdef DEBUG_SCHEDULER
122  qCDebug(MAILCOMMON_LOG) << " folder for task" << (*it) << "was deleted";
123 #endif
124  removeTask(it);
125  if (!mTaskList.isEmpty()) {
126  slotRunNextJob(); // to avoid the mess with invalid iterators :)
127  } else {
128  mTimer.stop();
129  }
130  return;
131  }
132 #ifdef DEBUG_SCHEDULER
133  qCDebug(MAILCOMMON_LOG) << " looking at folder" << folder.name();
134 #endif
135  task = *it;
136  removeTask(it);
137  break;
138  }
139 
140  if (!task) { // found nothing to run, i.e. folder was opened
141  return; // Timer keeps running, i.e. try again in 1 minute
142  }
143 
144  runTaskNow(task);
145  } // If nothing to do for that task, loop and find another one to run
146 }
147 
148 void JobScheduler::restartTimer()
149 {
150  if (mPendingImmediateTasks > 0) {
151  slotRunNextJob();
152  } else {
153 #ifdef DEBUG_SCHEDULER
154  mTimer.start(10000); // 10 seconds
155 #else
156  mTimer.start(1 * 60000); // 1 minute
157 #endif
158  }
159 }
160 
161 void JobScheduler::runTaskNow(ScheduledTask *task)
162 {
163  Q_ASSERT(mCurrentTask == nullptr);
164  if (mCurrentTask) {
165  interruptCurrentTask();
166  }
167  mCurrentTask = task;
168  mTimer.stop();
169  mCurrentJob = mCurrentTask->run();
170 #ifdef DEBUG_SCHEDULER
171  qCDebug(MAILCOMMON_LOG) << "JobScheduler: task" << mCurrentTask << "(type" << mCurrentTask->taskTypeId() << ")"
172  << "for folder" << mCurrentTask->folder()->label() << "returned job" << mCurrentJob << (mCurrentJob ? mCurrentJob->className() : 0);
173 #endif
174  if (!mCurrentJob) { // nothing to do, e.g. folder deleted
175  delete mCurrentTask;
176  mCurrentTask = nullptr;
177  if (!mTaskList.isEmpty()) {
178  restartTimer();
179  }
180  return;
181  }
182  // Register the job in the folder. This makes it autodeleted if the folder is deleted.
183 #if 0
184  mCurrentTask->folder()->storage()->addJob(mCurrentJob);
185 #endif
186  connect(mCurrentJob, &ScheduledJob::finished, this, &JobScheduler::slotJobFinished);
187  mCurrentJob->start();
188 }
189 
190 void JobScheduler::slotJobFinished()
191 {
192  // Do we need to test for mCurrentJob->error()? What do we do then?
193 #ifdef DEBUG_SCHEDULER
194  qCDebug(MAILCOMMON_LOG) << "JobScheduler: slotJobFinished";
195 #endif
196  delete mCurrentTask;
197  mCurrentTask = nullptr;
198  mCurrentJob = nullptr;
199  if (!mTaskList.isEmpty()) {
200  restartTimer();
201  }
202 }
203 
204 // D-Bus call to pause any background jobs
205 void JobScheduler::pause()
206 {
207  mPendingImmediateTasks = 0;
208  if (mCurrentJob && mCurrentJob->isCancellable()) {
209  interruptCurrentTask();
210  }
211  mTimer.stop();
212 }
213 
214 void JobScheduler::resume()
215 {
216  restartTimer();
217 }
218 
219 ////
220 
221 ScheduledJob::ScheduledJob(const Akonadi::Collection &folder, bool immediate)
222  : mImmediate(immediate)
223 {
224  mCancellable = true;
225  mSrcFolder = folder;
226 }
227 
228 ScheduledJob::~ScheduledJob() = default;
Akonadi::Collection folder() const
The folder which this task is supposed to handle, 0 if it was deleted meanwhile.
bool isEmpty() const const
bool isActive() const const
QVector::iterator begin()
bool isCancellable() const
Definition: folderjob.cpp:48
void append(const T &value)
A scheduled task is some information about a folder job that should be run later.
Definition: jobscheduler.h:33
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void registerTask(ScheduledTask *task)
Register a task to be done for a given folder.
void start(int msec)
void clear()
void timeout()
void start()
Start the job.
Definition: folderjob.cpp:28
virtual void kill()
Interrupt the job.
Definition: folderjob.cpp:37
void finished()
Emitted when the job finishes all processing.
QVector::iterator end()
ScheduledTask(const Akonadi::Collection &folder, bool immediate)
Creates a scheduled task for a given folder.
virtual int taskTypeId() const =0
An identifier for the type of task (a bit like QListViewItem::rtti).
void stop()
QVector::iterator erase(QVector::iterator begin, QVector::iterator end)
bool isValid() const
const QList< QKeySequence > & end()
The filter dialog.
virtual ScheduledJob * run()=0
Run this task, i.e.
QString name() const
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Sat Sep 24 2022 03:58:15 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.