File indexing completed on 2024-05-12 04:37:59

0001 /*
0002     SPDX-FileCopyrightText: 2007 Kris Wong <kris.p.wong@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-only
0005 */
0006 
0007 #ifndef KDEVPLATFORM_DUCHAINLOCK_H
0008 #define KDEVPLATFORM_DUCHAINLOCK_H
0009 
0010 #include <language/languageexport.h>
0011 #include <QScopedPointer>
0012 
0013 namespace KDevelop {
0014 // #define NO_DUCHAIN_LOCK_TESTING
0015 class DUChainLockPrivate;
0016 
0017 /**
0018  * Macros for ensuring the DUChain is locked properly.
0019  *
0020  * These should be used in every method that accesses or modifies a
0021  * member on the DUChain or one of its contexts, if ENSURE_CAN_WRITE and ENSURE_CAN_READ do not apply.
0022  * From within a Declaration or DUContext, ENSURE_CAN_WRITE and ENSURE_CAN_READ should be used instead of these.
0023  */
0024 #if !defined(NDEBUG) && !defined(NO_DUCHAIN_LOCK_TESTING)
0025 #define ENSURE_CHAIN_READ_LOCKED Q_ASSERT( \
0026         KDevelop::DUChain::lock()->currentThreadHasReadLock() || \
0027         KDevelop::DUChain::lock()->currentThreadHasWriteLock());
0028 #define ENSURE_CHAIN_WRITE_LOCKED Q_ASSERT(KDevelop::DUChain::lock()->currentThreadHasWriteLock());
0029 #define ENSURE_CHAIN_NOT_LOCKED Q_ASSERT( \
0030         !KDevelop::DUChain::lock()->currentThreadHasReadLock() && \
0031         !KDevelop::DUChain::lock()->currentThreadHasWriteLock());
0032 #else
0033 #define ENSURE_CHAIN_READ_LOCKED
0034 #define ENSURE_CHAIN_WRITE_LOCKED
0035 #define ENSURE_CHAIN_NOT_LOCKED
0036 #endif
0037 
0038 /**
0039  * Customized read/write locker for the definition-use chain.
0040  */
0041 class KDEVPLATFORMLANGUAGE_EXPORT DUChainLock
0042 {
0043 public:
0044     /// Constructor.
0045     DUChainLock();
0046     /// Destructor.
0047     ~DUChainLock();
0048 
0049     /**
0050      * Acquires a read lock. Will not return until the lock is acquired
0051      * or timeout
0052      *
0053      * Any number of read locks can be acquired at once, but not while
0054      * there is a write lock.  Read locks are recursive.
0055      * That means that a thread can acquire a read-lock when it already
0056      * has an arbitrary count of read- and write-locks acquired.
0057      * @param timeout A locking timeout in milliseconds. If it is reached, and the lock could not be acquired, false is returned. If null, the default timeout is used.
0058      */
0059     bool lockForRead(unsigned int timeout = 0);
0060 
0061     /**
0062      * Releases a previously acquired read lock.
0063      */
0064     void releaseReadLock();
0065 
0066     /**
0067      * Determines if the current thread has a read lock.
0068      */
0069     bool currentThreadHasReadLock();
0070 
0071     /**
0072      * Acquires a write lock. Will not return until the lock is acquired
0073      * or timeout is reached (10 seconds).
0074      *
0075      * Write locks are recursive. That means that they can by acquired by threads
0076      * that already have an arbitrary count of write-locks acquired.
0077      *
0078      * @param timeout A timeout in milliseconds. If zero, the default-timeout is used(Currently 10 seconds).
0079      *
0080      * \warning Write-locks can NOT be acquired by threads that already have a read-lock.
0081      */
0082     bool lockForWrite(unsigned int timeout = 0);
0083 
0084     /**
0085      * Releases a previously acquired write lock.
0086      */
0087     void releaseWriteLock();
0088 
0089     /**
0090      * Determines if the current thread has a write lock.
0091      */
0092     bool currentThreadHasWriteLock() const;
0093 
0094 private:
0095     const QScopedPointer<class DUChainLockPrivate> d_ptr;
0096     Q_DECLARE_PRIVATE(DUChainLock)
0097 };
0098 
0099 /**
0100  * Customized read locker for the definition-use chain.
0101  */
0102 class KDEVPLATFORMLANGUAGE_EXPORT DUChainReadLocker
0103 {
0104 public:
0105     /**
0106      * Constructor.  Attempts to acquire a read lock.
0107      *
0108      * \param duChainLock lock to read-acquire. If this is left zero, DUChain::lock() is used.
0109      * \param timeout Timeout in milliseconds. If this is not zero, you've got to check locked() to see whether the lock succeeded.
0110      */
0111     explicit DUChainReadLocker(DUChainLock* duChainLock = nullptr, unsigned int timeout = 0);
0112 
0113     /// Destructor.
0114     ~DUChainReadLocker();
0115 
0116     /// Acquire the read lock (again). Uses the same timeout given to the constructor.
0117     bool lock();
0118     /// Unlock the read lock.
0119     void unlock();
0120 
0121     ///Returns true if a lock was requested and the lock succeeded, else false
0122     bool locked() const;
0123 
0124 private:
0125     Q_DISABLE_COPY(DUChainReadLocker)
0126 
0127     ///This class does not use a d-pointer for performance reasons (allocation+deletion in every high frequency is expensive)
0128     DUChainLock* m_lock;
0129     bool m_locked;
0130     unsigned int m_timeout;
0131 };
0132 
0133 /**
0134  * Customized write locker for the definition-use chain.
0135  */
0136 class KDEVPLATFORMLANGUAGE_EXPORT DUChainWriteLocker
0137 {
0138 public:
0139     /**
0140      * Constructor.  Attempts to acquire a write lock.
0141      *
0142      * \param duChainLock lock to write-acquire. If this is left zero, DUChain::lock() is used.
0143      * \param timeout Timeout in milliseconds. If this is not zero, you've got to check locked() to see whether the lock succeeded.
0144      */
0145     explicit DUChainWriteLocker(DUChainLock* duChainLock = nullptr, unsigned int timeout = 0);
0146     /// Destructor.
0147     ~DUChainWriteLocker();
0148 
0149     /// Acquire the write lock (again). Uses the same timeout given to the constructor.
0150     bool lock();
0151     /// Unlock the write lock.
0152     void unlock();
0153 
0154     ///Returns true if a lock was requested and the lock succeeded, else false
0155     bool locked() const;
0156 
0157 private:
0158     Q_DISABLE_COPY(DUChainWriteLocker)
0159 
0160     ///This class does not use a d-pointer for performance reasons (allocation+deletion in every high frequency is expensive)
0161     DUChainLock* m_lock;
0162     bool m_locked;
0163     unsigned int m_timeout;
0164 };
0165 
0166 /**
0167  * Like the ENSURE_CHAIN_WRITE_LOCKED and .._READ_LOCKED, except that this should be used in items that can be detached from the du-chain, like DOContext's and Declarations.
0168  * Those items must implement an inDUChain() function that returns whether the item is in the du-chain.
0169  * Examples for such detachable items are DUContext's and Declarations, they can be written as long as they are not in the DUChain.
0170  * */
0171 #if !defined(NDEBUG) && !defined(NO_DUCHAIN_LOCK_TESTING)
0172 #define ENSURE_CAN_WRITE {if (inDUChain()) { ENSURE_CHAIN_WRITE_LOCKED }}
0173 #define ENSURE_CAN_READ {if (inDUChain()) { ENSURE_CHAIN_READ_LOCKED }}
0174 #else
0175 #define ENSURE_CAN_WRITE
0176 #define ENSURE_CAN_READ
0177 #endif
0178 }
0179 
0180 #endif // KDEVPLATFORM_DUCHAINLOCK_H