language/duchain
duchainlock.cpp
00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "duchainlock.h"
00022
00023
00024 #include <unistd.h>
00025 #include <QtCore/QThread>
00026 #include <QtCore/QMutex>
00027 #include <QtCore/QWaitCondition>
00028 #include <QtCore/QHash>
00029 #include <QtCore/QStack>
00030 #include <QTime>
00031 #include <util/google/dense_hash_map>
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044 #define LOCK_LOG_MILLISECONDS 1000
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054 #include <kdebug.h>
00055 #include <sys/time.h>
00056
00057 namespace std {
00058 #if defined(Q_CC_MSVC)
00059 using namespace stdext;
00060 #else
00061 using namespace __gnu_cxx;
00062 #endif
00063 }
00064
00065 #ifndef Q_CC_MSVC
00066 namespace __gnu_cxx {
00067 template<>
00068 struct hash<void *>
00069 {
00070 inline size_t operator() (const void *ptr) const { return reinterpret_cast<size_t>(ptr); }
00071 };
00072 }
00073 #endif
00074
00075 namespace KDevelop
00076 {
00077 class DUChainLockPrivate
00078 {
00079 public:
00080 DUChainLockPrivate() {
00081 m_writer = 0;
00082 m_writerRecursion = 0;
00083 m_totalReaderRecursion = 0;
00084 m_readers.set_empty_key(0);
00085 m_readersEnd = m_readers.end();
00086 }
00087
00091 bool haveOtherReaders() const {
00093 return m_totalReaderRecursion != ownReaderRecursion();
00094 }
00095
00096 int ownReaderRecursion() const {
00097 int ownReaderRecursion = 0;
00098
00099 ReaderMap::const_iterator it = m_readers.find( QThread::currentThreadId() );
00100 if( it != m_readersEnd )
00101 ownReaderRecursion = (*it).second;
00102 return ownReaderRecursion;
00103 }
00104
00105 #ifdef DEBUG_LOG_TIMING
00106 inline void auditTime() const {
00107 int ms = m_lockTime.elapsed();
00108 if (ms > LOCK_LOG_MILLISECONDS) {
00109 #ifndef DEBUG_LOG_BACKTRACE
00110 kWarning(9512) << "Long lock time:" << ms << "milliseconds." ;
00111 #else
00112 kWarning(9512) << "Long lock time:" << ms << "milliseconds, locked from:\n" << m_lockBacktrace ;
00113 #endif
00114 }
00115 }
00116 inline void startLockTiming() {
00117 m_lockTime.start();
00118 #ifdef DEBUG_LOG_BACKTRACE
00119 m_lockBacktrace = kBacktrace();
00120 #endif
00121 }
00122 #endif
00123
00124 QMutex m_mutex;
00125 Qt::HANDLE m_writer;
00126
00127 int m_writerRecursion;
00128 int m_totalReaderRecursion;
00129
00130 QWaitCondition m_waitForWriter;
00131
00133 typedef google::dense_hash_map<Qt::HANDLE, int> ReaderMap;
00134 ReaderMap m_readers;
00135 DUChainLockPrivate::ReaderMap::const_iterator m_readersEnd;
00136
00137 #ifdef DEBUG_LOG_TIMING
00138 QTime m_lockTime;
00139 #ifdef DEBUG_LOG_BACKTRACE
00140 QString m_lockBacktrace;
00141 #endif
00142 #endif
00143
00144 #ifdef DEBUG_ASSERTION_BACKTRACES
00145 QHash<Qt::HANDLE, QStack<QString> > m_readerBacktraces;
00146 #endif
00147 };
00148
00149 class DUChainWriteLockerPrivate
00150 {
00151 public:
00152 DUChainWriteLockerPrivate() : m_locked(false) {
00153 }
00154 DUChainLock* m_lock;
00155 bool m_locked;
00156 int m_timeout;
00157 };
00158
00159
00160 DUChainLock::DUChainLock()
00161 : d(new DUChainLockPrivate)
00162 {
00163 }
00164
00165 DUChainLock::~DUChainLock()
00166 {
00167 delete d;
00168 }
00169
00170 inline uint toMilliSeconds(timeval v) {
00171 return v.tv_sec * 1000 + v.tv_usec / 1000;
00172 }
00173
00174 bool DUChainLock::lockForRead(unsigned int timeout)
00175 {
00176 #ifdef DUCHAIN_LOCK_VERBOSE_OUTPUT
00177 kDebug(9505) << "DUChain read lock requested by thread:" << QThread::currentThreadId();
00178 #endif
00179 if(timeout == 0)
00180 timeout = 60000;
00181
00182 d->m_mutex.lock();
00183
00184 bool locked = false;
00185
00186 timeval startTime;
00187 gettimeofday(&startTime, 0);
00188
00189 while((d->m_writer && d->m_writer != QThread::currentThreadId())) {
00190 timeval currentTime;
00191 gettimeofday(¤tTime, 0);
00192 timeval waited;
00193 timersub(¤tTime, &startTime, &waited);
00194
00195 if(toMilliSeconds(waited) < timeout)
00196 d->m_waitForWriter.wait(&d->m_mutex, timeout);
00197 else
00198 break;
00199 }
00200
00201 if (d->m_writer == 0 || d->m_writer == QThread::currentThreadId()) {
00202 DUChainLockPrivate::ReaderMap::iterator it = d->m_readers.find( QThread::currentThreadId() );
00203 if ( it != d->m_readers.end() ) {
00204 ++(*it).second;
00205 } else {
00206 d->m_readers.insert( DUChainLockPrivate::ReaderMap::value_type(QThread::currentThreadId(), 1) );
00207 d->m_readersEnd = d->m_readers.end();
00208 }
00209 locked = true;
00210 }
00211
00212 if(locked) {
00213 ++d->m_totalReaderRecursion;
00214 #ifdef DEBUG_LOCK_TIMING
00215 d->startLockTiming();
00216 #endif
00217
00218 #ifdef DEBUG_ASSERTION_BACKTRACES
00219 d->m_readerBacktraces[QThread::currentThreadId()].push(kBacktrace());
00220 Q_ASSERT(d->m_readerBacktraces[QThread::currentThreadId()].size() == d->m_readers[QThread::currentThreadId()]);
00221 #endif
00222 }
00223
00224 d->m_mutex.unlock();
00225 return locked;
00226 }
00227
00228 bool DUChainLock::lockForRead() {
00229 bool ret = lockForRead(100000);
00230 Q_ASSERT(currentThreadHasReadLock());
00231 return ret;
00232 }
00233
00234 void DUChainLock::releaseReadLock()
00235 {
00236 #ifdef DUCHAIN_LOCK_VERBOSE_OUTPUT
00237 kDebug(9505) << "DUChain read lock released by thread:" << QThread::currentThreadId();
00238 #endif
00239
00240 QMutexLocker lock(&d->m_mutex);
00241 DUChainLockPrivate::ReaderMap::iterator it = d->m_readers.find( QThread::currentThreadId() );
00242 Q_ASSERT(it != d->m_readers.end());
00243 --(*it).second;
00244 Q_ASSERT((*it).second>=0);
00245 --d->m_totalReaderRecursion;
00246
00249
00250
00251
00252
00253 #ifdef DEBUG_ASSERTION_BACKTRACES
00254 d->m_readerBacktraces[QThread::currentThreadId()].pop();
00255 #endif
00256
00257 #ifdef DEBUG_LOCK_TIMING
00258 d->auditTime();
00259 #endif
00260 }
00261
00262 bool DUChainLock::currentThreadHasReadLock()
00263 {
00264 d->m_mutex.lock();
00265
00266 DUChainLockPrivate::ReaderMap::const_iterator it = d->m_readers.find( QThread::currentThreadId() );
00267 bool ret = false;
00268 if( it != d->m_readersEnd )
00269 ret = ((*it).second != 0);
00270
00271 d->m_mutex.unlock();
00272 return ret;
00273 }
00274
00275 bool DUChainLock::lockForWrite(uint timeout)
00276 {
00277 #ifdef DUCHAIN_LOCK_VERBOSE_OUTPUT
00278 kDebug(9505) << "DUChain write lock requested by thread:" << QThread::currentThreadId();
00279 kDebug(9505) << "Current backtrace:" << kBacktrace();
00280 #endif
00281
00282 if(timeout == 0)
00283 timeout = 10000;
00284
00285 QMutexLocker lock(&d->m_mutex);
00286
00287
00288 #ifdef DEBUG_ASSERTION_BACKTRACES
00289 if(d->ownReaderRecursion())
00290 kWarning(9505) << "Tried to lock the duchain for writing, but it was already locked for reading here:\n" << d->m_readerBacktraces[QThread::currentThreadId()].top();
00291 #endif
00292
00293 Q_ASSERT(d->ownReaderRecursion() == 0);
00294
00295 bool locked = false;
00296 uint currentTime = 0;
00297
00298
00299 while ( ( (d->m_writer && d->m_writer != QThread::currentThreadId()) || d->haveOtherReaders()) && currentTime < timeout) {
00300 lock.unlock();
00301 usleep(10000);
00302 currentTime++;
00303 lock.relock();
00304 #ifdef DEBUG_LOG_BACKTRACE
00305 d->auditTime();
00306 #endif
00307 }
00308
00309 if ( (d->m_writer == 0 || d->m_writer == QThread::currentThreadId()) && !d->haveOtherReaders()) {
00310 d->m_writer = QThread::currentThreadId();
00311 ++d->m_writerRecursion;
00312 locked = true;
00313 #ifdef DEBUG_LOCK_TIMING
00314 d->startLockTiming();
00315 #endif
00316 }
00317
00318 return locked;
00319 }
00320
00321 void DUChainLock::releaseWriteLock()
00322 {
00323 #ifdef DUCHAIN_LOCK_VERBOSE_OUTPUT
00324 kDebug(9505) << "DUChain write lock released by thread:" << QThread::currentThreadId();
00325 #endif
00326
00327 Q_ASSERT(currentThreadHasWriteLock());
00328 QMutexLocker lock(&d->m_mutex);
00329
00330 --d->m_writerRecursion;
00331
00332 if( !d->m_writerRecursion )
00333 d->m_writer = 0;
00334
00335 d->m_waitForWriter.wakeAll();
00336 #ifdef DEBUG_LOCK_TIMING
00337 d->auditTime();
00338 #endif
00339 }
00340
00341 bool DUChainLock::currentThreadHasWriteLock()
00342 {
00343 d->m_mutex.lock();
00344 bool ret = d->m_writer == QThread::currentThreadId();
00345 d->m_mutex.unlock();
00346 return ret;
00347 }
00348
00349
00350 DUChainReadLocker::DUChainReadLocker(DUChainLock* duChainLock, uint timeout) : m_locked(false), m_timeout(timeout)
00351 {
00352 m_lock = duChainLock;
00353 m_timeout = timeout;
00354 lock();
00355 }
00356
00357 DUChainReadLocker::~DUChainReadLocker()
00358 {
00359 unlock();
00360 }
00361
00362 bool DUChainReadLocker::locked() const {
00363 return m_locked;
00364 }
00365
00366 bool DUChainReadLocker::lock()
00367 {
00368 if( m_locked )
00369 return true;
00370
00371 bool l = false;
00372 if (m_lock) {
00373 l = m_lock->lockForRead(m_timeout);
00374 Q_ASSERT(m_timeout || l);
00375 };
00376
00377 m_locked = l;
00378
00379 return l;
00380 }
00381
00382 void DUChainReadLocker::unlock()
00383 {
00384 if (m_locked && m_lock) {
00385 m_lock->releaseReadLock();
00386 m_locked = false;
00387 }
00388 }
00389
00390
00391 DUChainWriteLocker::DUChainWriteLocker(DUChainLock* duChainLock, uint timeout)
00392 : d(new DUChainWriteLockerPrivate)
00393 {
00394 d->m_timeout = timeout;
00395 d->m_lock = duChainLock;
00396 lock();
00397 }
00398 DUChainWriteLocker::~DUChainWriteLocker()
00399 {
00400 unlock();
00401 delete d;
00402 }
00403
00404 bool DUChainWriteLocker::lock()
00405 {
00406 if( d->m_locked )
00407 return true;
00408
00409 bool l = false;
00410 if (d->m_lock) {
00411 l = d->m_lock->lockForWrite(d->m_timeout);
00412 Q_ASSERT(d->m_timeout || l);
00413 };
00414
00415 d->m_locked = l;
00416
00417 return l;
00418 }
00419
00420 bool DUChainWriteLocker::locked() const {
00421 return d->m_locked;
00422 }
00423
00424 void DUChainWriteLocker::unlock()
00425 {
00426 if (d->m_locked && d->m_lock) {
00427 d->m_lock->releaseWriteLock();
00428 d->m_locked = false;
00429 }
00430 }
00431 }
00432
00433
00434