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

KDE's Doxygen guidelines are available online.