File indexing completed on 2025-01-05 04:47:04

0001 /******************************************************************************
0002  *
0003  *  File : preprocessorinstance.cpp
0004  *  Creation date : Sat 18 Jul 2009 02:50:39
0005  *
0006  *  SPDX-FileCopyrightText: 2009 Szymon Stefanek <s.stefanek at gmail dot com>
0007  *
0008  *  SPDX-License-Identifier: LGPL-2.0-or-later
0009  *
0010  *****************************************************************************/
0011 
0012 #include "preprocessorinstance.h"
0013 #include "akonadiserver_debug.h"
0014 #include "preprocessorinterface.h"
0015 #include "preprocessormanager.h"
0016 
0017 #include "entities.h"
0018 
0019 #include "agentcontrolinterface.h"
0020 #include "agentmanagerinterface.h"
0021 
0022 #include "tracer.h"
0023 
0024 #include "private/dbus_p.h"
0025 
0026 using namespace Akonadi;
0027 using namespace Akonadi::Server;
0028 
0029 PreprocessorInstance::PreprocessorInstance(const QString &id, PreprocessorManager &manager, Tracer &tracer)
0030     : mManager(manager)
0031     , mTracer(tracer)
0032     , mId(id)
0033 {
0034     Q_ASSERT(!id.isEmpty());
0035 }
0036 
0037 PreprocessorInstance::~PreprocessorInstance() = default;
0038 
0039 bool PreprocessorInstance::init()
0040 {
0041     Q_ASSERT(!mBusy); // must be called very early
0042     Q_ASSERT(!mInterface);
0043 
0044     mInterface = new OrgFreedesktopAkonadiPreprocessorInterface(DBus::agentServiceName(mId, DBus::Preprocessor),
0045                                                                 QStringLiteral("/Preprocessor"),
0046                                                                 QDBusConnection::sessionBus(),
0047                                                                 this);
0048 
0049     if (!mInterface || !mInterface->isValid()) {
0050         mTracer.warning(
0051             QStringLiteral("PreprocessorInstance"),
0052             QStringLiteral("Could not connect to pre-processor instance '%1': %2").arg(mId, mInterface ? mInterface->lastError().message() : QString()));
0053         delete mInterface;
0054         mInterface = nullptr;
0055         return false;
0056     }
0057 
0058     QObject::connect(mInterface, &OrgFreedesktopAkonadiPreprocessorInterface::itemProcessed, this, &PreprocessorInstance::itemProcessed);
0059 
0060     return true;
0061 }
0062 
0063 void PreprocessorInstance::enqueueItem(qint64 itemId)
0064 {
0065     qCDebug(AKONADISERVER_LOG) << "PreprocessorInstance::enqueueItem(" << itemId << ")";
0066 
0067     mItemQueue.push_back(itemId);
0068 
0069     // If the preprocessor is already busy processing another item then do nothing.
0070     if (mBusy) {
0071         // The "head" item is the one being processed and we have just added another one.
0072         Q_ASSERT(mItemQueue.size() > 1);
0073         return;
0074     }
0075 
0076     // Not busy: handle the item.
0077     processHeadItem();
0078 }
0079 
0080 void PreprocessorInstance::processHeadItem()
0081 {
0082     // We shouldn't be called if there are no items in the queue
0083     Q_ASSERT(!mItemQueue.empty());
0084     // We shouldn't be here with no interface
0085     Q_ASSERT(mInterface);
0086 
0087     qint64 itemId = mItemQueue.front();
0088 
0089     // Fetch the actual item data (as it may have changed since it was enqueued)
0090     // The fetch will hit the cache if the item wasn't changed.
0091 
0092     PimItem actualItem = PimItem::retrieveById(itemId);
0093 
0094     while (!actualItem.isValid()) {
0095         // hum... item is gone ?
0096         mManager.preProcessorFinishedHandlingItem(this, itemId);
0097 
0098         mItemQueue.pop_front();
0099         if (mItemQueue.empty()) {
0100             // nothing more to process for this instance: jump out
0101             mBusy = false;
0102             return;
0103         }
0104 
0105         // try the next one in the queue
0106         itemId = mItemQueue.front();
0107         actualItem = PimItem::retrieveById(itemId);
0108     }
0109 
0110     // Ok.. got a valid item to process: collection and mimetype is known.
0111 
0112     qCDebug(AKONADISERVER_LOG) << "PreprocessorInstance::processHeadItem(): about to begin processing item " << itemId;
0113 
0114     mBusy = true;
0115 
0116     mItemProcessingStartDateTime = QDateTime::currentDateTime();
0117 
0118     // The beginProcessItem() D-Bus call is asynchronous (marked with NoReply attribute)
0119     mInterface->beginProcessItem(itemId, actualItem.collectionId(), actualItem.mimeType().name());
0120 
0121     qCDebug(AKONADISERVER_LOG) << "PreprocessorInstance::processHeadItem(): processing started for item " << itemId;
0122 }
0123 
0124 qint64 PreprocessorInstance::currentProcessingTime()
0125 {
0126     if (!mBusy) {
0127         return -1; // nothing being processed
0128     }
0129 
0130     return mItemProcessingStartDateTime.secsTo(QDateTime::currentDateTime());
0131 }
0132 
0133 bool PreprocessorInstance::abortProcessing()
0134 {
0135     Q_ASSERT_X(mBusy, "PreprocessorInstance::abortProcessing()", "You shouldn't call this method when isBusy() returns false");
0136 
0137     OrgFreedesktopAkonadiAgentControlInterface iface(DBus::agentServiceName(mId, DBus::Agent), QStringLiteral("/"), QDBusConnection::sessionBus(), this);
0138 
0139     if (!iface.isValid()) {
0140         mTracer.warning(QStringLiteral("PreprocessorInstance"),
0141                         QStringLiteral("Could not connect to pre-processor instance '%1': %2").arg(mId, iface.lastError().message()));
0142         return false;
0143     }
0144 
0145     // We don't check the return value.. as this is a "warning"
0146     // The preprocessor manager will check again in a while and eventually
0147     // terminate the agent at all...
0148     iface.abort();
0149 
0150     return true;
0151 }
0152 
0153 bool PreprocessorInstance::invokeRestart()
0154 {
0155     Q_ASSERT_X(mBusy, "PreprocessorInstance::invokeRestart()", "You shouldn't call this method when isBusy() returns false");
0156 
0157     OrgFreedesktopAkonadiAgentManagerInterface iface(DBus::serviceName(DBus::Control), QStringLiteral("/AgentManager"), QDBusConnection::sessionBus(), this);
0158 
0159     if (!iface.isValid()) {
0160         mTracer.warning(
0161             QStringLiteral("PreprocessorInstance"),
0162             QStringLiteral("Could not connect to the AgentManager in order to restart pre-processor instance '%1': %2").arg(mId, iface.lastError().message()));
0163         return false;
0164     }
0165 
0166     iface.restartAgentInstance(mId);
0167 
0168     return true;
0169 }
0170 
0171 void PreprocessorInstance::itemProcessed(qlonglong id)
0172 {
0173     qCDebug(AKONADISERVER_LOG) << "PreprocessorInstance::itemProcessed(" << id << ")";
0174 
0175     // We shouldn't be called if there are no items in the queue
0176     if (mItemQueue.empty()) {
0177         mTracer.warning(QStringLiteral("PreprocessorInstance"),
0178                         QStringLiteral("Pre-processor instance '%1' emitted itemProcessed(%2) but we actually have no item in the queue").arg(mId).arg(id));
0179         mBusy = false;
0180         return; // preprocessor is buggy (FIXME: What now ?)
0181     }
0182 
0183     // We should be busy now: this is more likely our fault, not the preprocessor's one.
0184     Q_ASSERT(mBusy);
0185 
0186     qlonglong itemId = mItemQueue.front();
0187 
0188     if (itemId != id) {
0189         mTracer.warning(
0190             QStringLiteral("PreprocessorInstance"),
0191             QStringLiteral("Pre-processor instance '%1' emitted itemProcessed(%2) but the head item in the queue has id %3").arg(mId).arg(id).arg(itemId));
0192 
0193         // FIXME: And what now ?
0194     }
0195 
0196     mItemQueue.pop_front();
0197 
0198     mManager.preProcessorFinishedHandlingItem(this, itemId);
0199 
0200     if (mItemQueue.empty()) {
0201         // Nothing more to do
0202         mBusy = false;
0203         return;
0204     }
0205 
0206     // Stay busy and process next item in the queue
0207     processHeadItem();
0208 }
0209 
0210 #include "moc_preprocessorinstance.cpp"