1 /*
2     SPDX-FileCopyrightText: 2007 Kris Wong <kris.p.wong@gmail.com>
3 
4     SPDX-License-Identifier: LGPL-2.0-only
5 */
6 
7 #ifndef KDEVPLATFORM_DUCHAINLOCK_H
8 #define KDEVPLATFORM_DUCHAINLOCK_H
9 
10 #include <language/languageexport.h>
11 #include <QScopedPointer>
12 
13 namespace KDevelop {
14 // #define NO_DUCHAIN_LOCK_TESTING
15 class DUChainLockPrivate;
16 
17 /**
18  * Macros for ensuring the DUChain is locked properly.
19  *
20  * These should be used in every method that accesses or modifies a
21  * member on the DUChain or one of its contexts, if ENSURE_CAN_WRITE and ENSURE_CAN_READ do not apply.
22  * From within a Declaration or DUContext, ENSURE_CAN_WRITE and ENSURE_CAN_READ should be used instead of these.
23  */
24 #if !defined(NDEBUG) && !defined(NO_DUCHAIN_LOCK_TESTING)
25 #define ENSURE_CHAIN_READ_LOCKED Q_ASSERT( \
26         KDevelop::DUChain::lock()->currentThreadHasReadLock() || \
27         KDevelop::DUChain::lock()->currentThreadHasWriteLock());
28 #define ENSURE_CHAIN_WRITE_LOCKED Q_ASSERT(KDevelop::DUChain::lock()->currentThreadHasWriteLock());
29 #define ENSURE_CHAIN_NOT_LOCKED Q_ASSERT( \
30         !KDevelop::DUChain::lock()->currentThreadHasReadLock() && \
31         !KDevelop::DUChain::lock()->currentThreadHasWriteLock());
32 #else
33 #define ENSURE_CHAIN_READ_LOCKED
34 #define ENSURE_CHAIN_WRITE_LOCKED
35 #define ENSURE_CHAIN_NOT_LOCKED
36 #endif
37 
38 /**
39  * Customized read/write locker for the definition-use chain.
40  */
41 class KDEVPLATFORMLANGUAGE_EXPORT DUChainLock
42 {
43 public:
44     /// Constructor.
45     DUChainLock();
46     /// Destructor.
47     ~DUChainLock();
48 
49     /**
50      * Acquires a read lock. Will not return until the lock is acquired
51      * or timeout
52      *
53      * Any number of read locks can be acquired at once, but not while
54      * there is a write lock.  Read locks are recursive.
55      * That means that a thread can acquire a read-lock when it already
56      * has an arbitrary count of read- and write-locks acquired.
57      * @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.
58      */
59     bool lockForRead(unsigned int timeout = 0);
60 
61     /**
62      * Releases a previously acquired read lock.
63      */
64     void releaseReadLock();
65 
66     /**
67      * Determines if the current thread has a read lock.
68      */
69     bool currentThreadHasReadLock();
70 
71     /**
72      * Acquires a write lock. Will not return until the lock is acquired
73      * or timeout is reached (10 seconds).
74      *
75      * Write locks are recursive. That means that they can by acquired by threads
76      * that already have an arbitrary count of write-locks acquired.
77      *
78      * @param timeout A timeout in milliseconds. If zero, the default-timeout is used(Currently 10 seconds).
79      *
80      * \warning Write-locks can NOT be acquired by threads that already have a read-lock.
81      */
82     bool lockForWrite(unsigned int timeout = 0);
83 
84     /**
85      * Releases a previously acquired write lock.
86      */
87     void releaseWriteLock();
88 
89     /**
90      * Determines if the current thread has a write lock.
91      */
92     bool currentThreadHasWriteLock() const;
93 
94 private:
95     const QScopedPointer<class DUChainLockPrivate> d_ptr;
96     Q_DECLARE_PRIVATE(DUChainLock)
97 };
98 
99 /**
100  * Customized read locker for the definition-use chain.
101  */
102 class KDEVPLATFORMLANGUAGE_EXPORT DUChainReadLocker
103 {
104 public:
105     /**
106      * Constructor.  Attempts to acquire a read lock.
107      *
108      * \param duChainLock lock to read-acquire. If this is left zero, DUChain::lock() is used.
109      * \param timeout Timeout in milliseconds. If this is not zero, you've got to check locked() to see whether the lock succeeded.
110      */
111     explicit DUChainReadLocker(DUChainLock* duChainLock = nullptr, unsigned int timeout = 0);
112 
113     /// Destructor.
114     ~DUChainReadLocker();
115 
116     /// Acquire the read lock (again). Uses the same timeout given to the constructor.
117     bool lock();
118     /// Unlock the read lock.
119     void unlock();
120 
121     ///Returns true if a lock was requested and the lock succeeded, else false
122     bool locked() const;
123 
124 private:
125     Q_DISABLE_COPY(DUChainReadLocker)
126 
127     ///This class does not use a d-pointer for performance reasons (allocation+deletion in every high frequency is expensive)
128     DUChainLock* m_lock;
129     bool m_locked;
130     unsigned int m_timeout;
131 };
132 
133 /**
134  * Customized write locker for the definition-use chain.
135  */
136 class KDEVPLATFORMLANGUAGE_EXPORT DUChainWriteLocker
137 {
138 public:
139     /**
140      * Constructor.  Attempts to acquire a write lock.
141      *
142      * \param duChainLock lock to write-acquire. If this is left zero, DUChain::lock() is used.
143      * \param timeout Timeout in milliseconds. If this is not zero, you've got to check locked() to see whether the lock succeeded.
144      */
145     explicit DUChainWriteLocker(DUChainLock* duChainLock = nullptr, unsigned int timeout = 0);
146     /// Destructor.
147     ~DUChainWriteLocker();
148 
149     /// Acquire the write lock (again). Uses the same timeout given to the constructor.
150     bool lock();
151     /// Unlock the write lock.
152     void unlock();
153 
154     ///Returns true if a lock was requested and the lock succeeded, else false
155     bool locked() const;
156 
157 private:
158     Q_DISABLE_COPY(DUChainWriteLocker)
159 
160     ///This class does not use a d-pointer for performance reasons (allocation+deletion in every high frequency is expensive)
161     DUChainLock* m_lock;
162     bool m_locked;
163     unsigned int m_timeout;
164 };
165 
166 /**
167  * 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.
168  * Those items must implement an inDUChain() function that returns whether the item is in the du-chain.
169  * Examples for such detachable items are DUContext's and Declarations, they can be written as long as they are not in the DUChain.
170  * */
171 #if !defined(NDEBUG) && !defined(NO_DUCHAIN_LOCK_TESTING)
172 #define ENSURE_CAN_WRITE {if (inDUChain()) { ENSURE_CHAIN_WRITE_LOCKED }}
173 #define ENSURE_CAN_READ {if (inDUChain()) { ENSURE_CHAIN_READ_LOCKED }}
174 #else
175 #define ENSURE_CAN_WRITE
176 #define ENSURE_CAN_READ
177 #endif
178 }
179 
180 #endif // KDEVPLATFORM_DUCHAINLOCK_H
181