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 }