File indexing completed on 2024-06-16 04:50:33

0001 /******************************************************************************
0002  *
0003  *  File : preprocessormanager.h
0004  *  Creation date : Sat 18 Jul 2009 01:58:50
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 #pragma once
0013 
0014 #include <QHash>
0015 #include <QList>
0016 #include <QMutex>
0017 #include <QObject>
0018 
0019 #include <deque>
0020 
0021 class QTimer;
0022 
0023 #include "preprocessorinstance.h"
0024 
0025 namespace Akonadi
0026 {
0027 namespace Server
0028 {
0029 class PimItem;
0030 class DataStore;
0031 class Tracer;
0032 
0033 /**
0034  * \class PreprocessorManager
0035  * \brief The manager for preprocessor agents
0036  *
0037  * This class takes care of synchronizing the preprocessor agents.
0038  *
0039  * The preprocessors see the incoming PimItem objects before the user
0040  * can see them (as long as the UI applications honor the hidden attribute).
0041  * The items are marked as hidden (by the Append and AkAppend
0042  * handlers) and then enqueued to the preprocessor chain via this class.
0043  * Once all the preprocessors have done their work the item is unhidden again.
0044  *
0045  * Preprocessing isn't designed for critical tasks. There may
0046  * be circumstances under that the Akonadi server fails to push an item
0047  * to all the preprocessors. Most notably after a server restart all
0048  * the items for that preprocessing was interrupted are just unhidden
0049  * without any attempt to resume the preprocessor jobs.
0050  *
0051  * The enqueue requests may or may not arrive from "inside" a database
0052  * transaction. The uncommitted transaction would "hide" the newly created items
0053  * from the preprocessor instances (which are separate processes).
0054  * This class, then, takes care of holding the newly arrived items
0055  * in a wait queue until their transaction is committed (or rolled back).
0056  */
0057 class PreprocessorManager : public QObject
0058 {
0059     friend class PreprocessorInstance;
0060 
0061     Q_OBJECT
0062     Q_CLASSINFO("D-Bus Interface", "org.freedesktop.Akonadi.PreprocessorManager")
0063 
0064 protected:
0065     /**
0066      * The hashtable of transaction wait queues. There is one wait
0067      * queue for each DataStore that is currently in a transaction.
0068      */
0069     QHash<const DataStore *, std::deque<qint64> *> mTransactionWaitQueueHash;
0070 
0071     /**
0072      * The preprocessor chain.
0073      * The pointers inside the list are owned.
0074      *
0075      * In all the algorithms we assume that this list is actually very short
0076      * (say 3-4 elements) and reverse lookup (pointer->index) is really fast.
0077      */
0078     QList<PreprocessorInstance *> mPreprocessorChain;
0079 
0080     /**
0081      * Is preprocessing enabled at all in this Akonadi server instance?
0082      * This is true by default and can be set via setEnabled().
0083      * Mainly used to disable preprocessing via configuration file.
0084      */
0085     bool mEnabled = false;
0086 
0087     /**
0088      * The mutex used to protect the internals of this class  (mainly
0089      * the mPreprocessorChain member).
0090      */
0091     QMutex mMutex;
0092 
0093     /**
0094      * The heartbeat timer. Used mainly to expire preprocessor jobs.
0095      */
0096     QTimer *mHeartbeatTimer = nullptr;
0097 
0098     Tracer &mTracer;
0099 
0100 public:
0101     /**
0102      * Creates an instance of PreprocessorManager
0103      */
0104     explicit PreprocessorManager(Tracer &tracer);
0105 
0106     /**
0107      * Destroys the instance of PreprocessorManager
0108      * and frees all the relevant resources
0109      */
0110     ~PreprocessorManager() override;
0111 
0112     /**
0113      * Returns true if preprocessing is active in this Akonadi server.
0114      * This means that we have at least one active preprocessor and
0115      * preprocessing hasn't been explicitly disabled via configuration
0116      * (so if isActive() returns true then also isEnabled() will return true).
0117      *
0118      * This function is thread-safe.
0119      */
0120     bool isActive();
0121 
0122     /**
0123      * Returns true if this preprocessor hasn't been explicitly disabled
0124      * via setEnabled( false ). This is used to disable preprocessing
0125      * via configuration even if we have a valid chain of preprocessors.
0126      *
0127      * Please note that this flag doesn't tell if we actually have
0128      * some registered preprocessors and thus we can do some meaningful job.
0129      * You should use isActive() for this purpose.
0130      */
0131     bool isEnabled() const
0132     {
0133         return mEnabled;
0134     }
0135 
0136     /**
0137      * Explicitly enables or disables the preprocessing in this Akonadi server.
0138      * The PreprocessorManager starts in enabled state but can be disabled
0139      * at a later stage: this is mainly used to disable preprocessing via
0140      * configuration.
0141      *
0142      * Please note that setting this to true doesn't interrupt the currently
0143      * running preprocessing jobs. Anything that was enqueued will be processed
0144      * anyway. However, in Akonadi this is only invoked very early,
0145      * when no preprocessors are alive yet.
0146      */
0147     void setEnabled(bool enabled)
0148     {
0149         mEnabled = enabled;
0150     }
0151 
0152     /**
0153      * Trigger the preprocessor chain for the specified item.
0154      * The item should have been added to the Akonadi database via
0155      * the specified DataStore object. If the DataStore is in a
0156      * transaction then this class will put the item in a wait
0157      * queue until the transaction is committed. If the transaction
0158      * is rolled back the whole wait queue will be discarded.
0159      * If the DataStore is not in a transaction then the item
0160      * will be pushed directly to the preprocessing chain.
0161      *
0162      * You should make sure that the preprocessor chain isActive()
0163      * before calling this method. The items you pass to this method,
0164      * also, should have the hidden attribute set.
0165      *
0166      * This function is thread-safe.
0167      */
0168     void beginHandleItem(const PimItem &item, const DataStore *dataStore);
0169 
0170     /**
0171      * This is called via D-Bus from AgentManager to register a preprocessor instance.
0172      *
0173      * This function is thread-safe.
0174      */
0175     void registerInstance(const QString &id);
0176 
0177     /**
0178      * This is called via D-Bus from AgentManager to unregister a preprocessor instance.
0179      *
0180      * This function is thread-safe.
0181      */
0182     void unregisterInstance(const QString &id);
0183 
0184 protected:
0185     /**
0186      * This is called by PreprocessorInstance to signal that a certain preprocessor has finished
0187      * handling an item.
0188      *
0189      * This function is thread-safe.
0190      */
0191     void preProcessorFinishedHandlingItem(PreprocessorInstance *preProcessor, qint64 itemId);
0192 
0193 private:
0194     /**
0195      * Finds the preprocessor instance by its identifier.
0196      *
0197      * This must be called with mMutex locked.
0198      */
0199     PreprocessorInstance *lockedFindInstance(const QString &id);
0200 
0201     /**
0202      * Pushes the specified item to the first preprocessor.
0203      * The caller *MUST* make sure that there is at least one preprocessor in the chain.
0204      */
0205     void lockedActivateFirstPreprocessor(qint64 itemId);
0206 
0207     /**
0208      * This is called internally to terminate the pre-processing
0209      * chain for the specified Item. All the preprocessors have
0210      * been triggered for it.
0211      *
0212      * This must be called with mMutex locked.
0213      */
0214     void lockedEndHandleItem(qint64 itemId);
0215 
0216     /**
0217      * This is the unprotected core of the unregisterInstance() function above.
0218      */
0219     void lockedUnregisterInstance(const QString &id);
0220 
0221     /**
0222      * Kill the wait queue for the specific DataStore object.
0223      */
0224     void lockedKillWaitQueue(const DataStore *dataStore, bool disconnectSlots);
0225 
0226 private Q_SLOTS:
0227 
0228     /**
0229      * Connected to the mHeartbeatTimer. Triggered every minute or something like that :D
0230      * Mainly used to expire preprocessor jobs.
0231      */
0232     void heartbeat();
0233 
0234     /**
0235      * This is used to handle database transactions and wait queues.
0236      * The call to this slot usually comes from a queued signal/slot connection
0237      * (i.e. from the *Append handler thread).
0238      */
0239     void dataStoreDestroyed();
0240 
0241     /**
0242      * This is used to handle database transactions and wait queues.
0243      * The call to this slot usually comes from a queued signal/slot connection
0244      * (i.e. from the *Append handler thread).
0245      */
0246     void dataStoreTransactionCommitted();
0247 
0248     /**
0249      * This is used to handle database transactions and wait queues.
0250      * The call to this slot usually comes from a queued signal/slot connection
0251      * (i.e. from the *Append handler thread).
0252      */
0253     void dataStoreTransactionRolledBack();
0254 
0255 }; // class PreprocessorManager
0256 
0257 } // namespace Server
0258 } // namespace Akonadi