From 86a237929e2b67ce333b635b760e78c628effb60 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Sat, 2 Jul 2011 15:13:12 +0200 Subject: QMutex is now just a pointer And added a POD QBasicMutex. (QBasicMutex* can safely be static_cast'ed to QMutex*) The d pointer is not anymore always a QMutexPrivate. If d == 0x0: the mutex is unlocked If d == 0x1: the mutex is locked, uncontended On linux: if d == 0x3: the mutex is locked contended, waiting on a futex If d is a pointer, it is a recursive mutex. On non-linux platforms: When a thread tries to lock a mutex for which d == 0x1, it will try to assing it a QMutexPrivated (allocated from a freelist) in order to wait for it. Change-Id: Ie1431cd9402a576fdd9a693cfd747166eebf5622 Reviewed-by: Bradley T. Hughes Reviewed-on: http://codereview.qt.nokia.com/2116 Reviewed-by: Qt Sanity Bot Reviewed-by: Olivier Goffart --- src/corelib/kernel/qobject.cpp | 8 +- src/corelib/thread/qmutex.cpp | 435 +++++++++++---------- src/corelib/thread/qmutex.h | 121 +++--- src/corelib/thread/qmutex_linux.cpp | 138 +++++++ src/corelib/thread/qmutex_mac.cpp | 96 +++++ src/corelib/thread/qmutex_p.h | 70 +++- src/corelib/thread/qmutex_unix.cpp | 116 +----- src/corelib/thread/qmutex_win.cpp | 10 +- src/corelib/thread/qorderedmutexlocker_p.h | 12 +- src/corelib/thread/qwaitcondition_unix.cpp | 2 +- src/corelib/thread/qwaitcondition_win.cpp | 2 +- src/corelib/thread/thread.pri | 9 +- tests/auto/qmutex/tst_qmutex.cpp | 111 +++++- .../corelib/thread/qmutex/tst_qmutex.cpp | 11 - 14 files changed, 682 insertions(+), 459 deletions(-) create mode 100644 src/corelib/thread/qmutex_linux.cpp create mode 100644 src/corelib/thread/qmutex_mac.cpp diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 27edbdacac..ab9314ce53 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -878,7 +878,7 @@ QObject::~QObject() if (c->next) c->next->prev = c->prev; } if (needToUnlock) - m->unlockInline(); + m->unlock(); connectionList.first = c->nextConnectionList; delete c; @@ -902,7 +902,7 @@ QObject::~QObject() bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m); //the node has maybe been removed while the mutex was unlocked in relock? if (!node || node->sender != sender) { - m->unlockInline(); + m->unlock(); continue; } node->receiver = 0; @@ -912,7 +912,7 @@ QObject::~QObject() node = node->next; if (needToUnlock) - m->unlockInline(); + m->unlock(); } } @@ -3076,7 +3076,7 @@ bool QMetaObjectPrivate::disconnectHelper(QObjectPrivate::Connection *c, } if (needToUnlock) - receiverMutex->unlockInline(); + receiverMutex->unlock(); c->receiver = 0; diff --git a/src/corelib/thread/qmutex.cpp b/src/corelib/thread/qmutex.cpp index 3e3bf8ff13..c90b44be6c 100644 --- a/src/corelib/thread/qmutex.cpp +++ b/src/corelib/thread/qmutex.cpp @@ -49,8 +49,25 @@ #include "qthread.h" #include "qmutex_p.h" +#ifndef Q_OS_LINUX +#include "private/qfreelist_p.h" +#endif + QT_BEGIN_NAMESPACE +/*! + \class QBasicMutex + \brief QMutex POD + \internal + + \ingroup thread + + - Can be used as global static object. + - Always non-recursive + - Do not use tryLock with timeout > 0, else you can have a leak (see the ~QMutex destructor) +*/ + + /*! \class QMutex \brief The QMutex class provides access serialization between threads. @@ -122,8 +139,12 @@ QT_BEGIN_NAMESPACE \sa lock(), unlock() */ QMutex::QMutex(RecursionMode mode) - : d(new QMutexPrivate(mode)) -{ } +{ + if (mode == Recursive) + d = new QRecursiveMutexPrivate; + else + d = 0; +} /*! Destroys the mutex. @@ -131,9 +152,18 @@ QMutex::QMutex(RecursionMode mode) \warning Destroying a locked mutex may result in undefined behavior. */ QMutex::~QMutex() -{ delete static_cast(d); } +{ + if (isRecursive()) + delete static_cast(d._q_value); + else if (d) { +#ifndef Q_OS_LINUX + if (d->possiblyUnlocked && tryLock()) { unlock(); return; } +#endif + qWarning("QMutex: destroying locked mutex"); + } +} -/*! +/*! \fn void QMutex::lock() Locks the mutex. If another thread has locked the mutex then this call will block until that thread has unlocked it. @@ -145,40 +175,8 @@ QMutex::~QMutex() \sa unlock() */ -void QMutex::lock() -{ - QMutexPrivate *d = static_cast(this->d); - Qt::HANDLE self; - - if (d->recursive) { - self = QThread::currentThreadId(); - if (d->owner == self) { - ++d->count; - Q_ASSERT_X(d->count != 0, "QMutex::lock", "Overflow in recursion counter"); - return; - } - bool isLocked = d->contenders.testAndSetAcquire(0, 1); - if (!isLocked) { - // didn't get the lock, wait for it - isLocked = d->wait(); - Q_ASSERT_X(isLocked, "QMutex::lock", - "Internal error, infinite wait has timed out."); - } - - d->owner = self; - ++d->count; - Q_ASSERT_X(d->count != 0, "QMutex::lock", "Overflow in recursion counter"); - return; - } - - bool isLocked = d->contenders.testAndSetAcquire(0, 1); - if (!isLocked) { - lockInternal(); - } -} - -/*! +/*!\fn bool QMutex::trylock() Attempts to lock the mutex. If the lock was obtained, this function returns true. If another thread has locked the mutex, this function returns false immediately. @@ -195,36 +193,9 @@ void QMutex::lock() \sa lock(), unlock() */ -bool QMutex::tryLock() -{ - QMutexPrivate *d = static_cast(this->d); - Qt::HANDLE self; - if (d->recursive) { - self = QThread::currentThreadId(); - if (d->owner == self) { - ++d->count; - Q_ASSERT_X(d->count != 0, "QMutex::tryLock", "Overflow in recursion counter"); - return true; - } - - bool isLocked = d->contenders.testAndSetAcquire(0, 1); - if (!isLocked) { - // some other thread has the mutex locked, or we tried to - // recursively lock an non-recursive mutex - return isLocked; - } - - d->owner = self; - ++d->count; - Q_ASSERT_X(d->count != 0, "QMutex::tryLock", "Overflow in recursion counter"); - return isLocked; - } - - return d->contenders.testAndSetAcquire(0, 1); -} - -/*! \overload +/*! \fn bool QMutex::tryLock(int timeout) + \overload Attempts to lock the mutex. This function returns true if the lock was obtained; otherwise it returns false. If another thread has @@ -247,81 +218,30 @@ bool QMutex::tryLock() \sa lock(), unlock() */ -bool QMutex::tryLock(int timeout) -{ - QMutexPrivate *d = static_cast(this->d); - Qt::HANDLE self; - if (d->recursive) { - self = QThread::currentThreadId(); - if (d->owner == self) { - ++d->count; - Q_ASSERT_X(d->count != 0, "QMutex::tryLock", "Overflow in recursion counter"); - return true; - } - bool isLocked = d->contenders.testAndSetAcquire(0, 1); - if (!isLocked) { - // didn't get the lock, wait for it - isLocked = d->wait(timeout); - if (!isLocked) - return false; - } - - d->owner = self; - ++d->count; - Q_ASSERT_X(d->count != 0, "QMutex::tryLock", "Overflow in recursion counter"); - return true; - } - - return (d->contenders.testAndSetAcquire(0, 1) - // didn't get the lock, wait for it - || d->wait(timeout)); -} - - -/*! +/*! \fn void QMutex::unlock() Unlocks the mutex. Attempting to unlock a mutex in a different thread to the one that locked it results in an error. Unlocking a mutex that is not locked results in undefined behavior. \sa lock() */ -void QMutex::unlock() -{ - QMutexPrivate *d = static_cast(this->d); - if (d->recursive) { - if (!--d->count) { - d->owner = 0; - if (!d->contenders.testAndSetRelease(1, 0)) - d->wakeUp(); - } - } else { - if (!d->contenders.testAndSetRelease(1, 0)) - d->wakeUp(); - } -} /*! - \fn bool QMutex::locked() - - Returns true if the mutex is locked by another thread; otherwise - returns false. - - It is generally a bad idea to use this function, because code - that uses it has a race condition. Use tryLock() and unlock() - instead. - - \oldcode - bool isLocked = mutex.locked(); - \newcode - bool isLocked = true; - if (mutex.tryLock()) { - mutex.unlock(); - isLocked = false; - } - \endcode + \fn void QMutex::isRecursive() + \since 5.0 + + Returns true if the mutex is recursive + */ +bool QBasicMutex::isRecursive() { + QMutexPrivate *d = this->d; + if (quintptr(d) <= 0x3) + return false; + return d->recursive; +} + /*! \class QMutexLocker @@ -418,96 +338,217 @@ void QMutex::unlock() \sa unlock() */ +#ifndef Q_OS_LINUX //linux implementation is in qmutex_linux.cpp /*! - \fn QMutex::QMutex(bool recursive) - - Use the constructor that takes a RecursionMode parameter instead. -*/ - -/*! - \internal helper for lockInline() + \internal helper for lock() */ -void QMutex::lockInternal() +bool QBasicMutex::lockInternal(int timeout) { - QMutexPrivate *d = static_cast(this->d); - - if (QThread::idealThreadCount() == 1) { - // don't spin on single cpu machines - bool isLocked = d->wait(); - Q_ASSERT_X(isLocked, "QMutex::lock", - "Internal error, infinite wait has timed out."); - Q_UNUSED(isLocked); - return; - } + while (!fastTryLock()) { + QMutexPrivate *d = this->d; + if (!d) // if d is 0, the mutex is unlocked + continue; - QElapsedTimer elapsedTimer; - elapsedTimer.start(); - do { - qint64 spinTime = elapsedTimer.nsecsElapsed(); - if (spinTime > d->maximumSpinTime) { - // didn't get the lock, wait for it, since we're not going to gain anything by spinning more - elapsedTimer.start(); - bool isLocked = d->wait(); - Q_ASSERT_X(isLocked, "QMutex::lock", - "Internal error, infinite wait has timed out."); - Q_UNUSED(isLocked); - - qint64 maximumSpinTime = d->maximumSpinTime; - qint64 averageWaitTime = d->averageWaitTime; - qint64 actualWaitTime = elapsedTimer.nsecsElapsed(); - if (actualWaitTime < (QMutexPrivate::MaximumSpinTimeThreshold * 3 / 2)) { - // measure the wait times - averageWaitTime = d->averageWaitTime = qMin((averageWaitTime + actualWaitTime) / 2, qint64(QMutexPrivate::MaximumSpinTimeThreshold)); + if (d == dummyLocked()) { + if (timeout == 0) + return false; + QMutexPrivate *newD = QMutexPrivate::allocate(); + if (!this->d.testAndSetOrdered(d, newD)) { + //Either the mutex is already unlocked, or another thread already set it. + newD->deref(); + continue; } + d = newD; + //the d->refCount is already 1 the deref will occurs when we unlock + } else if (d->recursive) { + return static_cast(d)->lock(timeout); + } + + if (timeout == 0 && !d->possiblyUnlocked) + return false; - // adjust the spin count when spinning does not benefit contention performance - if ((spinTime + actualWaitTime) - qint64(QMutexPrivate::MaximumSpinTimeThreshold) >= qint64(QMutexPrivate::MaximumSpinTimeThreshold)) { - // long waits, stop spinning - d->maximumSpinTime = 0; - } else { - // allow spinning if wait times decrease, but never spin more than the average wait time (otherwise we may perform worse) - d->maximumSpinTime = qBound(qint64(averageWaitTime * 3 / 2), maximumSpinTime / 2, qint64(QMutexPrivate::MaximumSpinTimeThreshold)); + if (!d->ref()) + continue; //that QMutexPrivate was already released + + if (d != this->d) { + //Either the mutex is already unlocked, or relocked with another mutex + d->deref(); + continue; + } + + int old_waiters; + do { + old_waiters = d->waiters; + if (old_waiters == -QMutexPrivate::BigNumber) { + // we are unlocking, and the thread that unlocks is about to change d to 0 + // we try to aquire the mutex by changing to dummyLocked() + if (this->d.testAndSetAcquire(d, dummyLocked())) { + // Mutex aquired + Q_ASSERT(d->waiters == -QMutexPrivate::BigNumber || d->waiters == 0); + d->waiters = 0; + d->deref(); + return true; + } else { + Q_ASSERT(d != this->d); //else testAndSetAcquire should have succeeded + // Mutex is likely to bo 0, we should continue the outer-loop, + // set old_waiters to the magic value of BigNumber + old_waiters = QMutexPrivate::BigNumber; + break; + } + } + } while (!d->waiters.testAndSetRelaxed(old_waiters, old_waiters + 1)); + + if (d != this->d) { + // Mutex was unlocked. + if (old_waiters != QMutexPrivate::BigNumber) { + //we did not break the previous loop + Q_ASSERT(d->waiters >= 1); + d->waiters.deref(); } - return; + d->deref(); + continue; + } + + if (d->wait(timeout)) { + if (d->possiblyUnlocked && d->possiblyUnlocked.testAndSetRelaxed(true, false)) + d->deref(); + d->derefWaiters(1); + //we got the lock. (do not deref) + Q_ASSERT(d == this->d); + return true; + } else { + Q_ASSERT(timeout >= 0); + //timeout + d->derefWaiters(1); + //There may be a race in which the mutex is unlocked right after we timed out, + // and before we deref the waiters, so maybe the mutex is actually unlocked. + if (!d->possiblyUnlocked.testAndSetRelaxed(false, true)) + d->deref(); + return false; } - // be a good citizen... yielding lets something else run if there is something to run, but may also relieve memory pressure if not - QThread::yieldCurrentThread(); - } while (d->contenders != 0 || !d->contenders.testAndSetAcquire(0, 1)); - - // spinning is working, do not change the spin time (unless we are using much less time than allowed to spin) - qint64 maximumSpinTime = d->maximumSpinTime; - qint64 spinTime = elapsedTimer.nsecsElapsed(); - if (spinTime < maximumSpinTime / 2) { - // we are using much less time than we need, adjust the limit - d->maximumSpinTime = qBound(qint64(d->averageWaitTime * 3 / 2), maximumSpinTime / 2, qint64(QMutexPrivate::MaximumSpinTimeThreshold)); } + Q_ASSERT(this->d); + return true; } /*! \internal */ -void QMutex::unlockInternal() +void QBasicMutex::unlockInternal() { - static_cast(d)->wakeUp(); + QMutexPrivate *d = this->d; + Q_ASSERT(d); //we must be locked + Q_ASSERT(d != dummyLocked()); // testAndSetRelease(dummyLocked(), 0) failed + + if (d->recursive) { + static_cast(d)->unlock(); + return; + } + + if (d->waiters.fetchAndAddRelease(-QMutexPrivate::BigNumber) == 0) { + //there is no one waiting on this mutex anymore, set the mutex as unlocked (d = 0) + if (this->d.testAndSetRelease(d, 0)) { + if (d->possiblyUnlocked && d->possiblyUnlocked.testAndSetRelaxed(true, false)) + d->deref(); + } + d->derefWaiters(0); + } else { + d->derefWaiters(0); + //there are thread waiting, transfer the lock. + d->wakeUp(); + } + d->deref(); } -/*! - \fn QMutex::lockInline() - \internal - inline version of QMutex::lock() -*/ +//The freelist managment +namespace { +struct FreeListConstants : QFreeListDefaultConstants { + enum { BlockCount = 4, MaxIndex=0xffff }; + static const int Sizes[BlockCount]; +}; +const int FreeListConstants::Sizes[FreeListConstants::BlockCount] = { + 16, + 128, + 1024, + FreeListConstants::MaxIndex - (16-128-1024) +}; + +typedef QFreeList FreeList; +Q_GLOBAL_STATIC(FreeList, freelist); +} + +QMutexPrivate *QMutexPrivate::allocate() +{ + int i = freelist()->next(); + QMutexPrivate *d = &(*freelist())[i]; + d->id = i; + Q_ASSERT(d->refCount == 0); + Q_ASSERT(!d->recursive); + Q_ASSERT(!d->possiblyUnlocked); + Q_ASSERT(d->waiters == 0); + d->refCount = 1; + return d; +} + +void QMutexPrivate::release() +{ + Q_ASSERT(!recursive); + Q_ASSERT(refCount == 0); + Q_ASSERT(!possiblyUnlocked); + Q_ASSERT(waiters == 0); + freelist()->release(id); +} + +// atomically substract "value" to the waiters, and remove the QMutexPrivate::BigNumber flag +void QMutexPrivate::derefWaiters(int value) +{ + int old_waiters; + int new_waiters; + do { + old_waiters = waiters; + new_waiters = old_waiters; + if (new_waiters < 0) { + new_waiters += QMutexPrivate::BigNumber; + } + new_waiters -= value; + } while (!waiters.testAndSetRelaxed(old_waiters, new_waiters)); +} +#endif /*! - \fn QMutex::unlockInline() \internal - inline version of QMutex::unlock() -*/ + */ +bool QRecursiveMutexPrivate::lock(int timeout) { + Qt::HANDLE self = QThread::currentThreadId(); + if (owner == self) { + ++count; + Q_ASSERT_X(count != 0, "QMutex::lock", "Overflow in recursion counter"); + return true; + } + bool success = true; + if (timeout == -1) { + mutex.lock(); + } else { + success = mutex.tryLock(timeout); + } + + if (success) + owner = self; + return success; +} /*! - \fn QMutex::tryLockInline() \internal - inline version of QMutex::tryLock() -*/ + */ +void QRecursiveMutexPrivate::unlock() +{ + if (count > 0) { + count--; + } else { + owner = 0; + mutex.unlock(); + } +} QT_END_NAMESPACE diff --git a/src/corelib/thread/qmutex.h b/src/corelib/thread/qmutex.h index cc667560db..a49b981d01 100644 --- a/src/corelib/thread/qmutex.h +++ b/src/corelib/thread/qmutex.h @@ -52,47 +52,64 @@ QT_BEGIN_NAMESPACE QT_MODULE(Core) -#ifndef QT_NO_THREAD +#if !defined(QT_NO_THREAD) && !defined(qdoc) -class QAtomicInt; -class QMutexData; +class QMutexPrivate; -class Q_CORE_EXPORT QMutex +class Q_CORE_EXPORT QBasicMutex { - friend class QWaitCondition; - friend class QWaitConditionPrivate; - public: - enum RecursionMode { NonRecursive, Recursive }; + inline void lock() { + if (!fastTryLock()) + lockInternal(); + } - explicit QMutex(RecursionMode mode = NonRecursive); - ~QMutex(); + inline void unlock() { + Q_ASSERT(d); //mutex must be locked + if (!d.testAndSetRelease(dummyLocked(), 0)) + unlockInternal(); + } + + bool tryLock(int timeout = 0) { + return fastTryLock() || lockInternal(timeout); + } - void lock(); //### Qt5: make inline; - inline void lockInline(); - bool tryLock(); //### Qt5: make inline; - bool tryLock(int timeout); - inline bool tryLockInline(); - void unlock(); //### Qt5: make inline; - inline void unlockInline(); + bool isRecursive(); private: - void lockInternal(); + inline bool fastTryLock() { + return d.testAndSetAcquire(0, dummyLocked()); + } + bool lockInternal(int timeout = -1); void unlockInternal(); - Q_DISABLE_COPY(QMutex) - QMutexData *d; + QBasicAtomicPointer d; + static inline QMutexPrivate *dummyLocked() { + return reinterpret_cast(quintptr(1)); + } + + friend class QMutex; + friend class QMutexPrivate; +}; + +class Q_CORE_EXPORT QMutex : public QBasicMutex { +public: + enum RecursionMode { NonRecursive, Recursive }; + explicit QMutex(RecursionMode mode = NonRecursive); + ~QMutex(); +private: + Q_DISABLE_COPY(QMutex) }; class Q_CORE_EXPORT QMutexLocker { public: - inline explicit QMutexLocker(QMutex *m) + inline explicit QMutexLocker(QBasicMutex *m) { Q_ASSERT_X((reinterpret_cast(m) & quintptr(1u)) == quintptr(0), "QMutexLocker", "QMutex pointer is misaligned"); if (m) { - m->lockInline(); + m->lock(); val = reinterpret_cast(m) | quintptr(1u); } else { val = 0; @@ -104,7 +121,7 @@ public: { if ((val & quintptr(1u)) == quintptr(1u)) { val &= ~quintptr(1u); - mutex()->unlockInline(); + mutex()->unlock(); } } @@ -112,7 +129,7 @@ public: { if (val) { if ((val & quintptr(1u)) == quintptr(0u)) { - mutex()->lockInline(); + mutex()->lock(); val |= quintptr(1u); } } @@ -138,54 +155,9 @@ private: quintptr val; }; -class QMutexData -{ - public: - QAtomicInt contenders; - const uint recursive : 1; - uint reserved : 31; - protected: - QMutexData(QMutex::RecursionMode mode); - ~QMutexData(); -}; - -#ifdef QT_NO_DEBUG -inline void QMutex::unlockInline() -{ - if (d->recursive) { - unlock(); - } else if (!d->contenders.testAndSetRelease(1, 0)) { - unlockInternal(); - } -} - -inline bool QMutex::tryLockInline() -{ - if (d->recursive) { - return tryLock(); - } else { - return d->contenders.testAndSetAcquire(0, 1); - } -} - -inline void QMutex::lockInline() -{ - if (d->recursive) { - lock(); - } else if(!tryLockInline()) { - lockInternal(); - } -} -#else // QT_NO_DEBUG -//in debug we do not use inline calls in order to allow debugging tools -// to hook the mutex locking functions. -inline void QMutex::unlockInline() { unlock(); } -inline bool QMutex::tryLockInline() { return tryLock(); } -inline void QMutex::lockInline() { lock(); } -#endif // QT_NO_DEBUG -#else // QT_NO_THREAD +#else // QT_NO_THREAD or qdoc class Q_CORE_EXPORT QMutex @@ -194,14 +166,11 @@ public: enum RecursionMode { NonRecursive, Recursive }; inline explicit QMutex(RecursionMode mode = NonRecursive) { Q_UNUSED(mode); } - inline ~QMutex() {} static inline void lock() {} - static inline void lockInline() {} static inline bool tryLock(int timeout = 0) { Q_UNUSED(timeout); return true; } - static inline bool tryLockInline() { return true; } static inline void unlock() {} - static inline void unlockInline() {} + static inline bool isRecursive() { return true; } private: Q_DISABLE_COPY(QMutex) @@ -221,7 +190,9 @@ private: Q_DISABLE_COPY(QMutexLocker) }; -#endif // QT_NO_THREAD +typedef QMutex QBasicMutex; + +#endif // QT_NO_THREAD or qdoc QT_END_NAMESPACE diff --git a/src/corelib/thread/qmutex_linux.cpp b/src/corelib/thread/qmutex_linux.cpp new file mode 100644 index 0000000000..17015b8f84 --- /dev/null +++ b/src/corelib/thread/qmutex_linux.cpp @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformdefs.h" +#include "qmutex.h" + +#ifndef QT_NO_THREAD +#include "qatomic.h" +#include "qmutex_p.h" +# include "qelapsedtimer.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static inline int _q_futex(QMutexPrivate *volatile *addr, int op, int val, const struct timespec *timeout) +{ + volatile int *int_addr = reinterpret_cast(addr); +#if Q_BYTE_ORDER == Q_BIG_ENDIAN && QT_POINTER_SIZE == 8 + int_addr++; //We want a pointer to the 32 least significant bit of QMutex::d +#endif + int *addr2 = 0; + int val2 = 0; + return syscall(SYS_futex, int_addr, op, val, timeout, addr2, val2); +} + +static inline QMutexPrivate *dummyFutexValue() +{ + return reinterpret_cast(quintptr(3)); +} + + +QMutexPrivate::~QMutexPrivate() {} +QMutexPrivate::QMutexPrivate(QMutex::RecursionMode mode) + : recursive(mode == QMutex::Recursive) {} + +bool QBasicMutex::lockInternal(int timeout) +{ + QElapsedTimer elapsedTimer; + if (timeout >= 1) + elapsedTimer.start(); + + while (!fastTryLock()) { + QMutexPrivate *d = this->d; + if (!d) // if d is 0, the mutex is unlocked + continue; + + if (quintptr(d) <= 0x3) { //d == dummyLocked() || d == dummyFutexValue() + if (timeout == 0) + return false; + while (this->d.fetchAndStoreAcquire(dummyFutexValue()) != 0) { + struct timespec ts, *pts = 0; + if (timeout >= 1) { + // recalculate the timeout + qint64 xtimeout = timeout * 1000 * 1000; + xtimeout -= elapsedTimer.nsecsElapsed(); + if (xtimeout <= 0) { + // timer expired after we returned + return false; + } + ts.tv_sec = xtimeout / Q_INT64_C(1000) / 1000 / 1000; + ts.tv_nsec = xtimeout % (Q_INT64_C(1000) * 1000 * 1000); + pts = &ts; + } + int r = _q_futex(&this->d._q_value, FUTEX_WAIT, quintptr(dummyFutexValue()), pts); + if (r != 0 && errno == ETIMEDOUT) + return false; + } + return true; + } + Q_ASSERT(d->recursive); + return static_cast(d)->lock(timeout); + } + Q_ASSERT(this->d); + return true; +} + +void QBasicMutex::unlockInternal() +{ + QMutexPrivate *d = this->d; + Q_ASSERT(d); //we must be locked + Q_ASSERT(d != dummyLocked()); // testAndSetRelease(dummyLocked(), 0) failed + + if (d == dummyFutexValue()) { + this->d.fetchAndStoreRelease(0); + _q_futex(&this->d._q_value, FUTEX_WAKE, 1, 0); + return; + } + + Q_ASSERT(d->recursive); + static_cast(d)->unlock(); +} + + +QT_END_NAMESPACE + +#endif // QT_NO_THREAD diff --git a/src/corelib/thread/qmutex_mac.cpp b/src/corelib/thread/qmutex_mac.cpp new file mode 100644 index 0000000000..f63feebca4 --- /dev/null +++ b/src/corelib/thread/qmutex_mac.cpp @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformdefs.h" +#include "qmutex.h" + +#if !defined(QT_NO_THREAD) + +#include "qmutex_p.h" + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +QMutexPrivate::QMutexPrivate(QMutex::RecursionMode mode) + : recursive(mode == QMutex::Recursive) +{ + kern_return_t r = semaphore_create(mach_task_self(), &mach_semaphore, SYNC_POLICY_FIFO, 0); + if (r != KERN_SUCCESS) + qWarning("QMutex: failed to create semaphore, error %d", r); +} + +QMutexPrivate::~QMutexPrivate() +{ + kern_return_t r = semaphore_destroy(mach_task_self(), mach_semaphore); + if (r != KERN_SUCCESS) + qWarning("QMutex: failed to destroy semaphore, error %d", r); +} + +bool QMutexPrivate::wait(int timeout) +{ + kern_return_t r; + if (timeout < 0) { + do { + r = semaphore_wait(mach_semaphore); + } while (r == KERN_ABORTED); + Q_ASSERT(r == KERN_SUCCESS); + } else { + mach_timespec_t ts; + ts.tv_nsec = ((timeout % 1000) * 1000) * 1000; + ts.tv_sec = (timeout / 1000); + r = semaphore_timedwait(mach_semaphore, ts); + } + return (r == KERN_SUCCESS); +} + +void QMutexPrivate::wakeUp() +{ + semaphore_signal(mach_semaphore); +} + + +QT_END_NAMESPACE + +#endif //QT_NO_THREAD diff --git a/src/corelib/thread/qmutex_p.h b/src/corelib/thread/qmutex_p.h index a9923c47a4..00f071ebef 100644 --- a/src/corelib/thread/qmutex_p.h +++ b/src/corelib/thread/qmutex_p.h @@ -57,50 +57,80 @@ #include #include #include +#include #if defined(Q_OS_MAC) # include #endif -#if defined(Q_OS_SYMBIAN) -# include -#endif - QT_BEGIN_NAMESPACE -class QMutexPrivate : public QMutexData { +class QMutexPrivate { public: - QMutexPrivate(QMutex::RecursionMode mode); ~QMutexPrivate(); + QMutexPrivate(QMutex::RecursionMode mode = QMutex::NonRecursive); bool wait(int timeout = -1); void wakeUp(); - // 1ms = 1000000ns - enum { MaximumSpinTimeThreshold = 1000000 }; - volatile qint64 maximumSpinTime; - volatile qint64 averageWaitTime; - Qt::HANDLE owner; - uint count; +#if !defined(Q_OS_LINUX) + // Conrol the lifetime of the privates + QAtomicInt refCount; + int id; + + bool ref() { + Q_ASSERT(refCount >= 0); + int c; + do { + c = refCount; + if (c == 0) + return false; + } while (!refCount.testAndSetRelaxed(c, c + 1)); + Q_ASSERT(refCount >= 0); + return true; + } + void deref() { + Q_ASSERT(refCount >=0); + if (!refCount.deref()) + release(); + Q_ASSERT(refCount >=0); + } + void release(); + static QMutexPrivate *allocate(); + + QAtomicInt waiters; //number of thread waiting + QAtomicInt possiblyUnlocked; //bool saying that a timed wait timed out + enum { BigNumber = 0x100000 }; //Must be bigger than the possible number of waiters (number of threads) + void derefWaiters(int value); +#endif + + // handle recursive mutex + bool recursive; + //platform specific stuff #if defined(Q_OS_MAC) semaphore_t mach_semaphore; -#elif defined(Q_OS_UNIX) && !defined(Q_OS_LINUX) && !defined(Q_OS_SYMBIAN) - volatile bool wakeup; +#elif defined(Q_OS_UNIX) && !defined(Q_OS_LINUX) + bool wakeup; pthread_mutex_t mutex; pthread_cond_t cond; #elif defined(Q_OS_WIN32) || defined(Q_OS_WINCE) HANDLE event; -#elif defined(Q_OS_SYMBIAN) - RSemaphore lock; #endif }; -inline QMutexData::QMutexData(QMutex::RecursionMode mode) - : recursive(mode == QMutex::Recursive) -{} +class QRecursiveMutexPrivate : public QMutexPrivate +{ +public: + QRecursiveMutexPrivate() + : QMutexPrivate(QMutex::Recursive), owner(0), count(0) {} + Qt::HANDLE owner; + uint count; + QMutex mutex; -inline QMutexData::~QMutexData() {} + bool lock(int timeout); + void unlock(); +}; QT_END_NAMESPACE diff --git a/src/corelib/thread/qmutex_unix.cpp b/src/corelib/thread/qmutex_unix.cpp index 2a9d23c361..0bccad589d 100644 --- a/src/corelib/thread/qmutex_unix.cpp +++ b/src/corelib/thread/qmutex_unix.cpp @@ -46,142 +46,35 @@ #ifndef QT_NO_THREAD #include "qatomic.h" #include "qmutex_p.h" - #include #if defined(Q_OS_VXWORKS) && defined(wakeup) #undef wakeup #endif -#if defined(Q_OS_MAC) -# include -# include -#elif defined(Q_OS_LINUX) -# include -# include -# include -# include -#endif - QT_BEGIN_NAMESPACE -#if !defined(Q_OS_MAC) && !defined(Q_OS_LINUX) static void report_error(int code, const char *where, const char *what) { if (code != 0) qWarning("%s: %s failure: %s", where, what, qPrintable(qt_error_string(code))); } -#endif - QMutexPrivate::QMutexPrivate(QMutex::RecursionMode mode) - : QMutexData(mode), maximumSpinTime(MaximumSpinTimeThreshold), averageWaitTime(0), owner(0), count(0) + : recursive(mode == QMutex::Recursive), wakeup(false) { -#if defined(Q_OS_MAC) - kern_return_t r = semaphore_create(mach_task_self(), &mach_semaphore, SYNC_POLICY_FIFO, 0); - if (r != KERN_SUCCESS) - qWarning("QMutex: failed to create semaphore, error %d", r); -#elif !defined(Q_OS_LINUX) - wakeup = false; report_error(pthread_mutex_init(&mutex, NULL), "QMutex", "mutex init"); report_error(pthread_cond_init(&cond, NULL), "QMutex", "cv init"); -#endif } QMutexPrivate::~QMutexPrivate() { -#if defined(Q_OS_MAC) - kern_return_t r = semaphore_destroy(mach_task_self(), mach_semaphore); - if (r != KERN_SUCCESS) - qWarning("QMutex: failed to destroy semaphore, error %d", r); -#elif !defined(Q_OS_LINUX) report_error(pthread_cond_destroy(&cond), "QMutex", "cv destroy"); report_error(pthread_mutex_destroy(&mutex), "QMutex", "mutex destroy"); -#endif } -#if defined(Q_OS_MAC) - bool QMutexPrivate::wait(int timeout) { - if (contenders.fetchAndAddAcquire(1) == 0) { - // lock acquired without waiting - return true; - } - kern_return_t r; - if (timeout < 0) { - do { - r = semaphore_wait(mach_semaphore); - } while (r == KERN_ABORTED); - if (r != KERN_SUCCESS) - qWarning("QMutex: infinite wait failed, error %d", r); - } else { - mach_timespec_t ts; - ts.tv_nsec = ((timeout % 1000) * 1000) * 1000; - ts.tv_sec = (timeout / 1000); - r = semaphore_timedwait(mach_semaphore, ts); - } - contenders.deref(); - return r == KERN_SUCCESS; -} - -void QMutexPrivate::wakeUp() -{ - semaphore_signal(mach_semaphore); -} - -#elif defined(Q_OS_LINUX) - -static inline int _q_futex(volatile int *addr, int op, int val, const struct timespec *timeout, int *addr2, int val2) -{ - return syscall(SYS_futex, addr, op, val, timeout, addr2, val2); -} - -bool QMutexPrivate::wait(int timeout) -{ - struct timespec ts, *pts = 0; - QElapsedTimer timer; - if (timeout >= 0) { - ts.tv_nsec = ((timeout % 1000) * 1000) * 1000; - ts.tv_sec = (timeout / 1000); - pts = &ts; - timer.start(); - } - while (contenders.fetchAndStoreAcquire(2) > 0) { - int r = _q_futex(&contenders._q_value, FUTEX_WAIT, 2, pts, 0, 0); - if (r != 0 && errno == ETIMEDOUT) - return false; - - if (pts) { - // recalculate the timeout - qint64 xtimeout = timeout * 1000 * 1000; - xtimeout -= timer.nsecsElapsed(); - if (xtimeout < 0) { - // timer expired after we returned - return false; - } - - ts.tv_sec = xtimeout / Q_INT64_C(1000) / 1000 / 1000; - ts.tv_nsec = xtimeout % (Q_INT64_C(1000) * 1000 * 1000); - } - } - return true; -} - -void QMutexPrivate::wakeUp() -{ - (void) contenders.fetchAndStoreRelease(0); - (void) _q_futex(&contenders._q_value, FUTEX_WAKE, 1, 0, 0, 0); -} - -#else // !Q_OS_MAC && !Q_OS_LINUX - -bool QMutexPrivate::wait(int timeout) -{ - if (contenders.fetchAndAddAcquire(1) == 0) { - // lock acquired without waiting - return true; - } report_error(pthread_mutex_lock(&mutex), "QMutex::lock", "mutex lock"); int errorCode = 0; while (!wakeup) { @@ -190,12 +83,10 @@ bool QMutexPrivate::wait(int timeout) } else { struct timeval tv; gettimeofday(&tv, 0); - timespec ti; ti.tv_nsec = (tv.tv_usec + (timeout % 1000) * 1000) * 1000; ti.tv_sec = tv.tv_sec + (timeout / 1000) + (ti.tv_nsec / 1000000000); ti.tv_nsec %= 1000000000; - errorCode = pthread_cond_timedwait(&cond, &mutex, &ti); } if (errorCode) { @@ -207,10 +98,10 @@ bool QMutexPrivate::wait(int timeout) report_error(errorCode, "QMutex::lock()", "cv wait"); } } + bool ret = wakeup; wakeup = false; report_error(pthread_mutex_unlock(&mutex), "QMutex::lock", "mutex unlock"); - contenders.deref(); - return errorCode == 0; + return ret; } void QMutexPrivate::wakeUp() @@ -221,7 +112,6 @@ void QMutexPrivate::wakeUp() report_error(pthread_mutex_unlock(&mutex), "QMutex::unlock", "mutex unlock"); } -#endif // !Q_OS_MAC && !Q_OS_LINUX QT_END_NAMESPACE diff --git a/src/corelib/thread/qmutex_win.cpp b/src/corelib/thread/qmutex_win.cpp index 53ad8cfe89..f6670f6d1e 100644 --- a/src/corelib/thread/qmutex_win.cpp +++ b/src/corelib/thread/qmutex_win.cpp @@ -48,7 +48,7 @@ QT_BEGIN_NAMESPACE QMutexPrivate::QMutexPrivate(QMutex::RecursionMode mode) - : QMutexData(mode), maximumSpinTime(MaximumSpinTimeThreshold), averageWaitTime(0), owner(0), count(0) + : recursive(mode) { event = CreateEvent(0, FALSE, FALSE, 0); if (!event) @@ -60,13 +60,7 @@ QMutexPrivate::~QMutexPrivate() bool QMutexPrivate::wait(int timeout) { - if (contenders.fetchAndAddAcquire(1) == 0) { - // lock acquired without waiting - return true; - } - bool returnValue = (WaitForSingleObject(event, timeout < 0 ? INFINITE : timeout) == WAIT_OBJECT_0); - contenders.deref(); - return returnValue; + return (WaitForSingleObject(event, timeout < 0 ? INFINITE : timeout) == WAIT_OBJECT_0); } void QMutexPrivate::wakeUp() diff --git a/src/corelib/thread/qorderedmutexlocker_p.h b/src/corelib/thread/qorderedmutexlocker_p.h index 8f01c1c056..e65a601369 100644 --- a/src/corelib/thread/qorderedmutexlocker_p.h +++ b/src/corelib/thread/qorderedmutexlocker_p.h @@ -79,8 +79,8 @@ public: void relock() { if (!locked) { - if (mtx1) mtx1->lockInline(); - if (mtx2) mtx2->lockInline(); + if (mtx1) mtx1->lock(); + if (mtx2) mtx2->lock(); locked = true; } } @@ -88,8 +88,8 @@ public: void unlock() { if (locked) { - if (mtx1) mtx1->unlockInline(); - if (mtx2) mtx2->unlockInline(); + if (mtx1) mtx1->unlock(); + if (mtx2) mtx2->unlock(); locked = false; } } @@ -100,10 +100,10 @@ public: if (mtx1 == mtx2) return false; if (mtx1 < mtx2) { - mtx2->lockInline(); + mtx2->lock(); return true; } - if (!mtx2->tryLockInline()) { + if (!mtx2->tryLock()) { mtx1->unlock(); mtx2->lock(); mtx1->lock(); diff --git a/src/corelib/thread/qwaitcondition_unix.cpp b/src/corelib/thread/qwaitcondition_unix.cpp index b946a9de24..dd85bf4023 100644 --- a/src/corelib/thread/qwaitcondition_unix.cpp +++ b/src/corelib/thread/qwaitcondition_unix.cpp @@ -146,7 +146,7 @@ bool QWaitCondition::wait(QMutex *mutex, unsigned long time) { if (! mutex) return false; - if (mutex->d->recursive) { + if (mutex->isRecursive()) { qWarning("QWaitCondition: cannot wait on recursive mutexes"); return false; } diff --git a/src/corelib/thread/qwaitcondition_win.cpp b/src/corelib/thread/qwaitcondition_win.cpp index 8a8db97342..bf1ec5e5ba 100644 --- a/src/corelib/thread/qwaitcondition_win.cpp +++ b/src/corelib/thread/qwaitcondition_win.cpp @@ -164,7 +164,7 @@ bool QWaitCondition::wait(QMutex *mutex, unsigned long time) { if (!mutex) return false; - if (mutex->d->recursive) { + if (mutex->isRecursive()) { qWarning("QWaitCondition::wait: Cannot wait on recursive mutexes"); return false; } diff --git a/src/corelib/thread/thread.pri b/src/corelib/thread/thread.pri index 592ab1644b..309bf30d25 100644 --- a/src/corelib/thread/thread.pri +++ b/src/corelib/thread/thread.pri @@ -24,8 +24,7 @@ SOURCES += thread/qatomic.cpp \ thread/qthread.cpp \ thread/qthreadstorage.cpp -unix:!symbian:SOURCES += thread/qmutex_unix.cpp \ - thread/qthread_unix.cpp \ +unix:!symbian:SOURCES += thread/qthread_unix.cpp \ thread/qwaitcondition_unix.cpp symbian:SOURCES += thread/qmutex_symbian.cpp \ @@ -39,3 +38,9 @@ win32:SOURCES += thread/qmutex_win.cpp \ integrity:SOURCES += thread/qmutex_unix.cpp \ thread/qthread_unix.cpp \ thread/qwaitcondition_unix.cpp + +unix: { + macx-* { SOURCES += thread/qmutex_mac.cpp } + else:linux-* { SOURCES += thread/qmutex_linux.cpp } + else { SOURCES += thread/qmutex_unix.cpp } +} diff --git a/tests/auto/qmutex/tst_qmutex.cpp b/tests/auto/qmutex/tst_qmutex.cpp index 5fed6bb44e..7ad6a98a4d 100644 --- a/tests/auto/qmutex/tst_qmutex.cpp +++ b/tests/auto/qmutex/tst_qmutex.cpp @@ -65,6 +65,7 @@ private slots: void stressTest(); void tryLockRace(); void qtbug16115_trylock(); + void moreStress(); }; static const int iterations = 100; @@ -83,6 +84,8 @@ QMutex normalMutex, recursiveMutex(QMutex::Recursive); QSemaphore testsTurn; QSemaphore threadsTurn; +enum { waitTime = 100 }; + void tst_QMutex::tryLock() { // test non-recursive mutex @@ -109,18 +112,18 @@ void tst_QMutex::tryLock() threadsTurn.acquire(); QTime timer; timer.start(); - QVERIFY(!normalMutex.tryLock(1000)); - QVERIFY(timer.elapsed() >= 1000); + QVERIFY(!normalMutex.tryLock(waitTime)); + QVERIFY(timer.elapsed() >= waitTime); testsTurn.release(); threadsTurn.acquire(); timer.start(); - QVERIFY(normalMutex.tryLock(1000)); - QVERIFY(timer.elapsed() <= 1000); + QVERIFY(normalMutex.tryLock(waitTime)); + QVERIFY(timer.elapsed() <= waitTime); QVERIFY(lockCount.testAndSetRelaxed(0, 1)); timer.start(); - QVERIFY(!normalMutex.tryLock(1000)); - QVERIFY(timer.elapsed() >= 1000); + QVERIFY(!normalMutex.tryLock(waitTime)); + QVERIFY(timer.elapsed() >= waitTime); QVERIFY(lockCount.testAndSetRelaxed(1, 0)); normalMutex.unlock(); testsTurn.release(); @@ -132,7 +135,7 @@ void tst_QMutex::tryLock() threadsTurn.acquire(); timer.start(); QVERIFY(normalMutex.tryLock(0)); - QVERIFY(timer.elapsed() < 1000); + QVERIFY(timer.elapsed() < waitTime); QVERIFY(lockCount.testAndSetRelaxed(0, 1)); QVERIFY(!normalMutex.tryLock(0)); QVERIFY(lockCount.testAndSetRelaxed(1, 0)); @@ -158,13 +161,13 @@ void tst_QMutex::tryLock() normalMutex.unlock(); threadsTurn.release(); - // thread can't acquire lock, timeout = 1000 + // thread can't acquire lock, timeout = waitTime testsTurn.acquire(); normalMutex.lock(); QVERIFY(lockCount.testAndSetRelaxed(0, 1)); threadsTurn.release(); - // thread can acquire lock, timeout = 1000 + // thread can acquire lock, timeout = waitTime testsTurn.acquire(); QVERIFY(lockCount.testAndSetRelaxed(1, 0)); normalMutex.unlock(); @@ -215,17 +218,17 @@ void tst_QMutex::tryLock() threadsTurn.acquire(); QTime timer; timer.start(); - QVERIFY(!recursiveMutex.tryLock(1000)); - QVERIFY(timer.elapsed() >= 1000); + QVERIFY(!recursiveMutex.tryLock(waitTime)); + QVERIFY(timer.elapsed() >= waitTime); QVERIFY(!recursiveMutex.tryLock(0)); testsTurn.release(); threadsTurn.acquire(); timer.start(); - QVERIFY(recursiveMutex.tryLock(1000)); - QVERIFY(timer.elapsed() <= 1000); + QVERIFY(recursiveMutex.tryLock(waitTime)); + QVERIFY(timer.elapsed() <= waitTime); QVERIFY(lockCount.testAndSetRelaxed(0, 1)); - QVERIFY(recursiveMutex.tryLock(1000)); + QVERIFY(recursiveMutex.tryLock(waitTime)); QVERIFY(lockCount.testAndSetRelaxed(1, 2)); QVERIFY(lockCount.testAndSetRelaxed(2, 1)); recursiveMutex.unlock(); @@ -241,7 +244,7 @@ void tst_QMutex::tryLock() threadsTurn.acquire(); timer.start(); QVERIFY(recursiveMutex.tryLock(0)); - QVERIFY(timer.elapsed() < 1000); + QVERIFY(timer.elapsed() < waitTime); QVERIFY(lockCount.testAndSetRelaxed(0, 1)); QVERIFY(recursiveMutex.tryLock(0)); QVERIFY(lockCount.testAndSetRelaxed(1, 2)); @@ -274,7 +277,7 @@ void tst_QMutex::tryLock() recursiveMutex.unlock(); threadsTurn.release(); - // thread can't acquire lock, timeout = 1000 + // thread can't acquire lock, timeout = waitTime testsTurn.acquire(); recursiveMutex.lock(); QVERIFY(lockCount.testAndSetRelaxed(0, 1)); @@ -282,7 +285,7 @@ void tst_QMutex::tryLock() QVERIFY(lockCount.testAndSetRelaxed(1, 2)); threadsTurn.release(); - // thread can acquire lock, timeout = 1000 + // thread can acquire lock, timeout = waitTime testsTurn.acquire(); QVERIFY(lockCount.testAndSetRelaxed(2, 1)); recursiveMutex.unlock(); @@ -436,7 +439,8 @@ void tst_QMutex::lock_unlock_locked_tryLock() } } -enum { one_minute = 60 * 1000, threadCount = 10 }; +enum { one_minute = 6 * 1000, //not really one minute, but else it is too long. + threadCount = 10 }; class StressTestThread : public QThread { @@ -497,7 +501,7 @@ public: do { if (mutex.tryLock()) mutex.unlock(); - } while (t.elapsed() < 20000); + } while (t.elapsed() < one_minute/2); } }; QMutex TryLockRaceThread::mutex; @@ -534,7 +538,7 @@ void tst_QMutex::qtbug16115_trylock() TrylockThread(QMutex &mut) : mut(mut) {} QMutex &mut; void run() { - for (int i = 0; i < 1000000; ++i) { + for (int i = 0; i < 100000; ++i) { if (mut.tryLock(0)) { if ((++qtbug16115_trylock_counter) != 1) ++qtbug16115_failure_count; @@ -553,7 +557,7 @@ void tst_QMutex::qtbug16115_trylock() t2.start(); t3.start(); - for (int i = 0; i < 1000000; ++i) { + for (int i = 0; i < 100000; ++i) { mut.lock(); if ((++qtbug16115_trylock_counter) != 1) ++qtbug16115_failure_count; @@ -567,5 +571,70 @@ void tst_QMutex::qtbug16115_trylock() QCOMPARE(qtbug16115_failure_count, 0); } + +class MoreStressTestThread : public QThread +{ + QTime t; +public: + static QAtomicInt lockCount; + static QAtomicInt sentinel[threadCount]; + static QMutex mutex[threadCount]; + static QAtomicInt errorCount; + void start() + { + t.start(); + QThread::start(); + } + void run() + { + quint64 i = 0; + while (t.elapsed() < one_minute) { + i++; + uint nb = (i * 9 + lockCount * 13) % threadCount; + QMutexLocker locker(&mutex[nb]); + if (sentinel[nb]) errorCount.ref(); + if (sentinel[nb].fetchAndAddRelaxed(5)) errorCount.ref(); + if (!sentinel[nb].testAndSetRelaxed(5, 0)) errorCount.ref(); + if (sentinel[nb]) errorCount.ref(); + lockCount.ref(); + nb = (nb * 17 + i * 5 + lockCount * 3) % threadCount; + if (mutex[nb].tryLock()) { + if (sentinel[nb]) errorCount.ref(); + if (sentinel[nb].fetchAndAddRelaxed(16)) errorCount.ref(); + if (!sentinel[nb].testAndSetRelaxed(16, 0)) errorCount.ref(); + if (sentinel[nb]) errorCount.ref(); + lockCount.ref(); + mutex[nb].unlock(); + } + nb = (nb * 15 + i * 47 + lockCount * 31) % threadCount; + if (mutex[nb].tryLock(2)) { + if (sentinel[nb]) errorCount.ref(); + if (sentinel[nb].fetchAndAddRelaxed(53)) errorCount.ref(); + if (!sentinel[nb].testAndSetRelaxed(53, 0)) errorCount.ref(); + if (sentinel[nb]) errorCount.ref(); + lockCount.ref(); + mutex[nb].unlock(); + } + } + } +}; +QMutex MoreStressTestThread::mutex[threadCount]; +QAtomicInt MoreStressTestThread::lockCount; +QAtomicInt MoreStressTestThread::sentinel[threadCount]; +QAtomicInt MoreStressTestThread::errorCount = 0; + +void tst_QMutex::moreStress() +{ + MoreStressTestThread threads[threadCount]; + for (int i = 0; i < threadCount; ++i) + threads[i].start(); + QVERIFY(threads[0].wait(one_minute + 10000)); + for (int i = 1; i < threadCount; ++i) + QVERIFY(threads[i].wait(10000)); + qDebug("locked %d times", int(MoreStressTestThread::lockCount)); + QCOMPARE(int(MoreStressTestThread::errorCount), 0); +} + + QTEST_MAIN(tst_QMutex) #include "tst_qmutex.moc" diff --git a/tests/benchmarks/corelib/thread/qmutex/tst_qmutex.cpp b/tests/benchmarks/corelib/thread/qmutex/tst_qmutex.cpp index 05a15750c1..05e184d0c2 100644 --- a/tests/benchmarks/corelib/thread/qmutex/tst_qmutex.cpp +++ b/tests/benchmarks/corelib/thread/qmutex/tst_qmutex.cpp @@ -151,7 +151,6 @@ void tst_QMutex::noThread_data() QTest::addColumn("t"); QTest::newRow("noLock") << 1; - QTest::newRow("QMutexInline") << 2; QTest::newRow("QMutex") << 3; QTest::newRow("QMutexLocker") << 4; } @@ -172,16 +171,6 @@ void tst_QMutex::noThread() } } break; - case 2: - QBENCHMARK { - count = 0; - for (int i = 0; i < N; i++) { - mtx.lockInline(); - count++; - mtx.unlockInline(); - } - } - break; case 3: QBENCHMARK { count = 0; -- cgit v1.2.3