7#include "collectionscheduler.h"
8#include "akonadiserver_debug.h"
9#include "storage/datastore.h"
10#include "storage/selectquerybuilder.h"
12#include "private/tristate_p.h"
18using namespace std::literals::chrono_literals;
28class PauseableTimer :
public QTimer
38 void start(std::chrono::milliseconds
interval)
41 mPaused = QDateTime();
48 start(std::chrono::milliseconds{
interval()});
53 mStarted = QDateTime();
54 mPaused = QDateTime();
74 const auto remainder = std::chrono::milliseconds{
interval()} - std::chrono::seconds{mStarted.secsTo(mPaused)};
75 start(qMax(std::chrono::milliseconds{0}, remainder));
76 mPaused = QDateTime();
83 return mPaused.isValid();
94using namespace Akonadi::Server;
97 : AkThread(threadName, priority, parent)
101CollectionScheduler::~CollectionScheduler()
106void CollectionScheduler::quit()
109 mScheduler =
nullptr;
114void CollectionScheduler::inhibit(
bool inhibit)
127int CollectionScheduler::minimumInterval()
const
132CollectionScheduler::TimePoint CollectionScheduler::nextScheduledTime(qint64 collectionId)
const
134 QMutexLocker locker(&mScheduleLock);
135 const auto i = constFind(collectionId);
136 if (i != mSchedule.cend()) {
142std::chrono::milliseconds CollectionScheduler::currentTimerInterval()
const
144 return std::chrono::milliseconds(mScheduler->isActive() ? mScheduler->interval() : 0);
147void CollectionScheduler::setMinimumInterval(
int intervalMinutes)
150 mMinInterval = intervalMinutes;
153void CollectionScheduler::collectionAdded(qint64 collectionId)
155 Collection collection = Collection::retrieveById(collectionId);
157 if (shouldScheduleCollection(collection)) {
160 [
this, collection]() {
161 scheduleCollection(collection);
167void CollectionScheduler::collectionChanged(qint64 collectionId)
169 QMutexLocker locker(&mScheduleLock);
170 const auto it = constFind(collectionId);
171 if (it != mSchedule.cend()) {
172 const Collection &oldCollection = it.value();
173 Collection changed = Collection::retrieveById(collectionId);
175 if (hasChanged(oldCollection, changed)) {
176 if (shouldScheduleCollection(changed)) {
182 scheduleCollection(changed);
188 collectionRemoved(collectionId);
193 collectionAdded(collectionId);
197void CollectionScheduler::collectionRemoved(qint64 collectionId)
199 QMutexLocker locker(&mScheduleLock);
200 auto it = find(collectionId);
201 if (it != mSchedule.end()) {
202 const bool reschedule = it == mSchedule.begin();
213void CollectionScheduler::startScheduler()
215 QMutexLocker locker(&mScheduleLock);
217 if (mScheduler->isPaused()) {
221 if (mSchedule.isEmpty()) {
228 const auto next = mSchedule.constBegin().key();
230 const auto delayUntilNext = std::chrono::duration_cast<std::chrono::milliseconds>(next - std::chrono::steady_clock::now());
231 mScheduler->start(qMax(std::chrono::milliseconds{0}, delayUntilNext));
235void CollectionScheduler::scheduleCollection(Collection collection,
bool shouldStartScheduler)
239 QMutexLocker locker(&mScheduleLock);
240 auto i = find(collection.id());
241 if (i != mSchedule.end()) {
245 if (!shouldScheduleCollection(collection)) {
249 const int expireMinutes = qMax(mMinInterval, collectionScheduleInterval(collection));
250 TimePoint nextCheck(std::chrono::steady_clock::now() + std::chrono::minutes(expireMinutes));
255 auto it = constLowerBound(nextCheck);
256 if (it != mSchedule.cend() && it.key() - nextCheck < 1min) {
257 nextCheck = it.key();
261 }
else if (it != mSchedule.cbegin()) {
263 if (nextCheck - it.key() < 1min) {
264 nextCheck = it.key();
268 mSchedule.insert(nextCheck, collection);
269 if (shouldStartScheduler && !mScheduler->isActive()) {
275CollectionScheduler::ScheduleMap::const_iterator CollectionScheduler::constFind(qint64 collectionId)
const
277 return std::find_if(mSchedule.cbegin(), mSchedule.cend(), [collectionId](
const Collection &c) {
278 return c.id() == collectionId;
282CollectionScheduler::ScheduleMap::iterator CollectionScheduler::find(qint64 collectionId)
284 return std::find_if(mSchedule.begin(), mSchedule.end(), [collectionId](
const Collection &c) {
285 return c.id() == collectionId;
290CollectionScheduler::ScheduleMap::const_iterator CollectionScheduler::constLowerBound(TimePoint timestamp)
const
292 return mSchedule.lowerBound(timestamp);
296void CollectionScheduler::init()
300 mScheduler =
new PauseableTimer();
301 mScheduler->setSingleShot(
true);
306 SelectQueryBuilder<Collection> qb;
308 qCWarning(AKONADISERVER_LOG) <<
"Failed to query initial collections for scheduler!";
309 qCWarning(AKONADISERVER_LOG) <<
"Not a fatal error, no collections will be scheduled for sync or cache expiration!";
313 for (
const Collection &collection : collections) {
314 scheduleCollection(collection);
321void CollectionScheduler::schedulerTimeout()
323 QMutexLocker locker(&mScheduleLock);
328 const auto timestamp = mSchedule.constBegin().key();
329 const QList<Collection> collections = mSchedule.values(timestamp);
330 mSchedule.
remove(timestamp);
333 for (
const Collection &collection : collections) {
334 collectionExpired(collection);
335 scheduleCollection(collection,
false);
341#include "collectionscheduler.moc"
343#include "moc_collectionscheduler.cpp"
QList< Collection > List
Describes a list of collections.
static DataStore * self()
Per thread singleton.
virtual void activeCachePolicy(Collection &col)
Determines the active cache policy for this Collection.
bool exec()
Executes the query, returns true on success.
QList< T > result()
Returns the result of this SELECT query.
Helper integration between Akonadi and Qt.
QAction * next(const QObject *recvr, const char *slot, QObject *parent)
QDateTime currentDateTimeUtc()
void remove(qsizetype i, qsizetype n)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * parent() const const
bool isActive() const const