File indexing completed on 2024-05-05 04:38:44
0001 /* 0002 SPDX-FileCopyrightText: 2010 David Nolden <david.nolden.kdevelop@art-master.de> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "foregroundlock.h" 0008 0009 #include <QCoreApplication> 0010 #include <QMutex> 0011 #include <QThread> 0012 0013 using namespace KDevelop; 0014 0015 namespace { 0016 0017 QMutex internalMutex; 0018 QMutex tryLockMutex; 0019 QMutex waitMutex; 0020 QMutex finishMutex; 0021 QWaitCondition condition; 0022 0023 volatile QThread* holderThread = nullptr; 0024 volatile int recursion = 0; 0025 0026 void lockForegroundMutexInternal() 0027 { 0028 if (holderThread == QThread::currentThread()) { 0029 // We already have the mutex 0030 ++recursion; 0031 } else { 0032 internalMutex.lock(); 0033 Q_ASSERT(recursion == 0 && holderThread == nullptr); 0034 holderThread = QThread::currentThread(); 0035 recursion = 1; 0036 } 0037 } 0038 0039 bool tryLockForegroundMutexInternal(int interval = 0) 0040 { 0041 if (holderThread == QThread::currentThread()) { 0042 // We already have the mutex 0043 ++recursion; 0044 return true; 0045 } else { 0046 if (internalMutex.tryLock(interval)) { 0047 Q_ASSERT(recursion == 0 && holderThread == nullptr); 0048 holderThread = QThread::currentThread(); 0049 recursion = 1; 0050 return true; 0051 } else { 0052 return false; 0053 } 0054 } 0055 } 0056 0057 void unlockForegroundMutexInternal(bool duringDestruction = false) 0058 { 0059 /// Note: QThread::currentThread() might already be invalid during destruction. 0060 if (!duringDestruction) { 0061 Q_ASSERT(holderThread == QThread::currentThread()); 0062 } 0063 0064 Q_ASSERT(recursion > 0); 0065 recursion -= 1; 0066 if (recursion == 0) { 0067 holderThread = nullptr; 0068 internalMutex.unlock(); 0069 } 0070 } 0071 } 0072 0073 ForegroundLock::ForegroundLock(bool lock) 0074 { 0075 if (lock) 0076 relock(); 0077 } 0078 0079 void KDevelop::ForegroundLock::relock() 0080 { 0081 Q_ASSERT(!m_locked); 0082 0083 if (!QCoreApplication::instance() || // Initialization isn't complete yet 0084 QThread::currentThread() == QCoreApplication::instance()->thread() 0085 || // We're the main thread (deadlock might happen if we'd enter the trylock loop) 0086 holderThread == QThread::currentThread()) { // We already have the foreground lock (deadlock might happen if 0087 // we'd enter the trylock loop) 0088 lockForegroundMutexInternal(); 0089 } else { 0090 QMutexLocker lock(&tryLockMutex); 0091 0092 while (!tryLockForegroundMutexInternal(10)) { 0093 // In case an additional event-loop was started from within the foreground, we send 0094 // events to the foreground to temporarily release the lock. 0095 0096 class ForegroundReleaser : public DoInForeground 0097 { 0098 public: 0099 void doInternal() override 0100 { 0101 // By locking the mutex, we make sure that the requester is actually waiting for the condition 0102 waitMutex.lock(); 0103 // Now we release the foreground lock 0104 TemporarilyReleaseForegroundLock release; 0105 // And signalize to the requester that we've released it 0106 condition.wakeAll(); 0107 // Allow the requester to actually wake up, by unlocking m_waitMutex 0108 waitMutex.unlock(); 0109 // Now wait until the requester is ready 0110 QMutexLocker lock(&finishMutex); 0111 } 0112 }; 0113 0114 static ForegroundReleaser releaser; 0115 0116 QMutexLocker lockWait(&waitMutex); 0117 QMutexLocker lockFinish(&finishMutex); 0118 0119 QMetaObject::invokeMethod(&releaser, "doInternalSlot", Qt::QueuedConnection); 0120 // We limit the waiting time here, because sometimes it may happen that the foreground-lock is released, 0121 // and the foreground is waiting without an event-loop running. (For example through TemporarilyReleaseForegroundLock) 0122 condition.wait(&waitMutex, 30); 0123 0124 if (tryLockForegroundMutexInternal()) { 0125 //success 0126 break; 0127 } else { 0128 //Probably a third thread has creeped in and 0129 //got the foreground lock before us. Just try again. 0130 } 0131 } 0132 } 0133 m_locked = true; 0134 Q_ASSERT(holderThread == QThread::currentThread()); 0135 Q_ASSERT(recursion > 0); 0136 } 0137 0138 bool KDevelop::ForegroundLock::isLockedForThread() 0139 { 0140 return QThread::currentThread() == holderThread 0141 || QThread::currentThread() == QCoreApplication::instance()->thread(); 0142 } 0143 0144 bool KDevelop::ForegroundLock::tryLock() 0145 { 0146 if (tryLockForegroundMutexInternal()) { 0147 m_locked = true; 0148 return true; 0149 } 0150 return false; 0151 } 0152 0153 void KDevelop::ForegroundLock::unlock() 0154 { 0155 Q_ASSERT(m_locked); 0156 unlockForegroundMutexInternal(); 0157 m_locked = false; 0158 } 0159 0160 TemporarilyReleaseForegroundLock::TemporarilyReleaseForegroundLock() 0161 { 0162 Q_ASSERT(holderThread == QThread::currentThread()); 0163 0164 m_recursion = 0; 0165 0166 while (holderThread == QThread::currentThread()) { 0167 unlockForegroundMutexInternal(); 0168 ++m_recursion; 0169 } 0170 } 0171 0172 TemporarilyReleaseForegroundLock::~TemporarilyReleaseForegroundLock() 0173 { 0174 for (int a = 0; a < m_recursion; ++a) 0175 lockForegroundMutexInternal(); 0176 0177 Q_ASSERT(recursion == m_recursion && holderThread == QThread::currentThread()); 0178 } 0179 0180 KDevelop::ForegroundLock::~ForegroundLock() 0181 { 0182 if (m_locked) 0183 unlock(); 0184 } 0185 0186 bool KDevelop::ForegroundLock::isLocked() const 0187 { 0188 return m_locked; 0189 } 0190 0191 namespace KDevelop { 0192 void DoInForeground::doIt() 0193 { 0194 if (QThread::currentThread() == QCoreApplication::instance()->thread()) { 0195 // We're already in the foreground, just call the handler code 0196 doInternal(); 0197 } else { 0198 QMutexLocker lock(&m_mutex); 0199 QMetaObject::invokeMethod(this, "doInternalSlot", Qt::QueuedConnection); 0200 m_wait.wait(&m_mutex); 0201 } 0202 } 0203 0204 DoInForeground::~DoInForeground() 0205 { 0206 } 0207 0208 DoInForeground::DoInForeground() 0209 { 0210 moveToThread(QCoreApplication::instance()->thread()); 0211 } 0212 0213 void DoInForeground::doInternalSlot() 0214 { 0215 VERIFY_FOREGROUND_LOCKED 0216 doInternal(); 0217 QMutexLocker lock(&m_mutex); 0218 m_wait.wakeAll(); 0219 } 0220 } 0221 0222 // Important: The foreground lock has to be held by default, so lock it during static initialization 0223 static struct StaticLock 0224 { 0225 StaticLock() 0226 { 0227 lockForegroundMutexInternal(); 0228 } 0229 ~StaticLock() 0230 { 0231 unlockForegroundMutexInternal(true); 0232 } 0233 private: 0234 Q_DISABLE_COPY(StaticLock) 0235 } staticLock; 0236 0237 #include "moc_foregroundlock.cpp"