File indexing completed on 2024-05-12 04:37:59
0001 /* 0002 SPDX-FileCopyrightText: 2007 Kris Wong <kris.p.wong@gmail.com> 0003 SPDX-FileCopyrightText: 2007 Hamish Rodda <rodda@kde.org> 0004 SPDX-FileCopyrightText: 2007-2009 David Nolden <david.nolden.kdevelop@art-master.de> 0005 SPDX-FileCopyrightText: 2013 Milian Wolff <mail@milianw.de> 0006 0007 SPDX-License-Identifier: LGPL-2.0-only 0008 */ 0009 0010 #include "duchainlock.h" 0011 #include "duchain.h" 0012 0013 #include <QThread> 0014 #include <QThreadStorage> 0015 #include <QElapsedTimer> 0016 0017 ///@todo Always prefer exactly that lock that is requested by the thread that has the foreground mutex, 0018 /// to reduce the amount of UI blocking. 0019 0020 //Microseconds to sleep when waiting for a lock 0021 const uint uSleepTime = 500; 0022 0023 namespace KDevelop { 0024 class DUChainLockPrivate 0025 { 0026 public: 0027 DUChainLockPrivate() 0028 : m_writer(nullptr) 0029 , m_writerRecursion(0) 0030 , m_totalReaderRecursion(0) 0031 { } 0032 0033 int ownReaderRecursion() const 0034 { 0035 return m_readerRecursion.localData(); 0036 } 0037 0038 void changeOwnReaderRecursion(int difference) 0039 { 0040 m_readerRecursion.localData() += difference; 0041 Q_ASSERT(m_readerRecursion.localData() >= 0); 0042 m_totalReaderRecursion.fetchAndAddOrdered(difference); 0043 } 0044 0045 ///Holds the writer that currently has the write-lock, or zero. Is protected by m_writerRecursion. 0046 QAtomicPointer<QThread> m_writer; 0047 0048 ///How often is the chain write-locked by the writer? This value protects m_writer, 0049 ///m_writer may only be changed by the thread that successfully increases this value from 0 to 1 0050 QAtomicInt m_writerRecursion; 0051 ///How often is the chain read-locked recursively by all readers? Should be sum of all m_readerRecursion values 0052 QAtomicInt m_totalReaderRecursion; 0053 0054 QThreadStorage<int> m_readerRecursion; 0055 }; 0056 0057 DUChainLock::DUChainLock() 0058 : d_ptr(new DUChainLockPrivate) 0059 { 0060 } 0061 0062 DUChainLock::~DUChainLock() = default; 0063 0064 bool DUChainLock::lockForRead(unsigned int timeout) 0065 { 0066 Q_D(DUChainLock); 0067 0068 ///Step 1: Increase the own reader-recursion. This will make sure no further write-locks will succeed 0069 d->changeOwnReaderRecursion(1); 0070 0071 QThread* w = d->m_writer.loadAcquire(); 0072 if (w == nullptr || w == QThread::currentThread()) { 0073 //Successful lock: Either there is no writer, or we hold the write-lock by ourselves 0074 } else { 0075 ///Step 2: Start spinning until there is no writer any more 0076 0077 QElapsedTimer t; 0078 if (timeout) { 0079 t.start(); 0080 } 0081 0082 while (d->m_writer.loadAcquire()) { 0083 if (!timeout || t.elapsed() < timeout) { 0084 QThread::usleep(uSleepTime); 0085 } else { 0086 //Fail! 0087 d->changeOwnReaderRecursion(-1); 0088 return false; 0089 } 0090 } 0091 } 0092 0093 return true; 0094 } 0095 0096 void DUChainLock::releaseReadLock() 0097 { 0098 Q_D(DUChainLock); 0099 0100 d->changeOwnReaderRecursion(-1); 0101 } 0102 0103 bool DUChainLock::currentThreadHasReadLock() 0104 { 0105 Q_D(DUChainLock); 0106 0107 return ( bool )d->ownReaderRecursion(); 0108 } 0109 0110 bool DUChainLock::lockForWrite(uint timeout) 0111 { 0112 Q_D(DUChainLock); 0113 0114 //It is not allowed to acquire a write-lock while holding read-lock 0115 0116 Q_ASSERT(d->ownReaderRecursion() == 0); 0117 0118 if (d->m_writer.loadRelaxed() == QThread::currentThread()) { 0119 //We already hold the write lock, just increase the recursion count and return 0120 d->m_writerRecursion.fetchAndAddRelaxed(1); 0121 return true; 0122 } 0123 0124 QElapsedTimer t; 0125 if (timeout) { 0126 t.start(); 0127 } 0128 0129 while (1) { 0130 //Try acquiring the write-lcok 0131 if (d->m_totalReaderRecursion.loadRelaxed() == 0 && d->m_writerRecursion.testAndSetOrdered(0, 1)) { 0132 //Now we can be sure that there is no other writer, as we have increased m_writerRecursion from 0 to 1 0133 d->m_writer = QThread::currentThread(); 0134 if (d->m_totalReaderRecursion.loadRelaxed() == 0) { 0135 //There is still no readers, we have successfully acquired a write-lock 0136 return true; 0137 } else { 0138 //There may be readers.. we have to continue spinning 0139 d->m_writer = nullptr; 0140 d->m_writerRecursion = 0; 0141 } 0142 } 0143 0144 if (!timeout || t.elapsed() < timeout) { 0145 QThread::usleep(uSleepTime); 0146 } else { 0147 //Fail! 0148 return false; 0149 } 0150 } 0151 0152 return false; 0153 } 0154 0155 void DUChainLock::releaseWriteLock() 0156 { 0157 Q_D(DUChainLock); 0158 0159 Q_ASSERT(currentThreadHasWriteLock()); 0160 0161 //The order is important here, m_writerRecursion protects m_writer 0162 0163 //TODO: could testAndSet here 0164 if (d->m_writerRecursion.loadRelaxed() == 1) { 0165 d->m_writer = nullptr; 0166 d->m_writerRecursion = 0; 0167 } else { 0168 d->m_writerRecursion.fetchAndAddOrdered(-1); 0169 } 0170 } 0171 0172 bool DUChainLock::currentThreadHasWriteLock() const 0173 { 0174 Q_D(const DUChainLock); 0175 0176 return d->m_writer.loadRelaxed() == QThread::currentThread(); 0177 } 0178 0179 DUChainReadLocker::DUChainReadLocker(DUChainLock* duChainLock, uint timeout) 0180 : m_lock(duChainLock ? duChainLock : DUChain::lock()) 0181 , m_locked(false) 0182 , m_timeout(timeout) 0183 { 0184 lock(); 0185 } 0186 0187 DUChainReadLocker::~DUChainReadLocker() 0188 { 0189 unlock(); 0190 } 0191 0192 bool DUChainReadLocker::locked() const 0193 { 0194 return m_locked; 0195 } 0196 0197 bool DUChainReadLocker::lock() 0198 { 0199 if (m_locked) { 0200 return true; 0201 } 0202 0203 bool l = false; 0204 if (m_lock) { 0205 l = m_lock->lockForRead(m_timeout); 0206 Q_ASSERT(m_timeout || l); 0207 } 0208 ; 0209 0210 m_locked = l; 0211 0212 return l; 0213 } 0214 0215 void DUChainReadLocker::unlock() 0216 { 0217 if (m_locked && m_lock) { 0218 m_lock->releaseReadLock(); 0219 m_locked = false; 0220 } 0221 } 0222 0223 DUChainWriteLocker::DUChainWriteLocker(DUChainLock* duChainLock, uint timeout) 0224 : m_lock(duChainLock ? duChainLock : DUChain::lock()) 0225 , m_locked(false) 0226 , m_timeout(timeout) 0227 { 0228 lock(); 0229 } 0230 0231 DUChainWriteLocker::~DUChainWriteLocker() 0232 { 0233 unlock(); 0234 } 0235 0236 bool DUChainWriteLocker::lock() 0237 { 0238 if (m_locked) { 0239 return true; 0240 } 0241 0242 bool l = false; 0243 if (m_lock) { 0244 l = m_lock->lockForWrite(m_timeout); 0245 Q_ASSERT(m_timeout || l); 0246 } 0247 ; 0248 0249 m_locked = l; 0250 0251 return l; 0252 } 0253 0254 bool DUChainWriteLocker::locked() const 0255 { 0256 return m_locked; 0257 } 0258 0259 void DUChainWriteLocker::unlock() 0260 { 0261 if (m_locked && m_lock) { 0262 m_lock->releaseWriteLock(); 0263 m_locked = false; 0264 } 0265 } 0266 }