summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOlivier Goffart <olivier.goffart@nokia.com>2011-07-02 15:13:12 +0200
committerQt by Nokia <qt-info@nokia.com>2011-07-29 10:32:07 +0200
commit86a237929e2b67ce333b635b760e78c628effb60 (patch)
tree290ad9a01a9bcf19ef02f4455728823d136b7193
parent487583459ea7958f24cd579888a662bcce26caf3 (diff)
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 <bradley.hughes@nokia.com> Reviewed-on: http://codereview.qt.nokia.com/2116 Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com> Reviewed-by: Olivier Goffart <olivier.goffart@nokia.com>
-rw-r--r--src/corelib/kernel/qobject.cpp8
-rw-r--r--src/corelib/thread/qmutex.cpp435
-rw-r--r--src/corelib/thread/qmutex.h121
-rw-r--r--src/corelib/thread/qmutex_linux.cpp138
-rw-r--r--src/corelib/thread/qmutex_mac.cpp96
-rw-r--r--src/corelib/thread/qmutex_p.h70
-rw-r--r--src/corelib/thread/qmutex_unix.cpp116
-rw-r--r--src/corelib/thread/qmutex_win.cpp10
-rw-r--r--src/corelib/thread/qorderedmutexlocker_p.h12
-rw-r--r--src/corelib/thread/qwaitcondition_unix.cpp2
-rw-r--r--src/corelib/thread/qwaitcondition_win.cpp2
-rw-r--r--src/corelib/thread/thread.pri9
-rw-r--r--tests/auto/qmutex/tst_qmutex.cpp111
-rw-r--r--tests/benchmarks/corelib/thread/qmutex/tst_qmutex.cpp11
14 files changed, 682 insertions, 459 deletions
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,9 +49,26 @@
#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<QMutexPrivate *>(d); }
+{
+ if (isRecursive())
+ delete static_cast<QRecursiveMutexPrivate *>(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<QMutexPrivate *>(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<QMutexPrivate *>(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<QMutexPrivate *>(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<QMutexPrivate *>(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<QMutexPrivate *>(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<QRecursiveMutexPrivate *>(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<QMutexPrivate *>(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<QRecursiveMutexPrivate *>(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<QMutexPrivate, FreeListConstants> 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<QMutexPrivate> d;
+ static inline QMutexPrivate *dummyLocked() {
+ return reinterpret_cast<QMutexPrivate *>(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<quintptr>(m) & quintptr(1u)) == quintptr(0),
"QMutexLocker", "QMutex pointer is misaligned");
if (m) {
- m->lockInline();
+ m->lock();
val = reinterpret_cast<quintptr>(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 <linux/futex.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <errno.h>
+
+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<volatile int *>(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<QMutexPrivate *>(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<QRecursiveMutexPrivate *>(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<QRecursiveMutexPrivate *>(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 <mach/mach.h>
+#include <mach/task.h>
+
+#include <errno.h>
+
+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 <QtCore/qglobal.h>
#include <QtCore/qnamespace.h>
#include <QtCore/qmutex.h>
+#include <QtCore/qatomic.h>
#if defined(Q_OS_MAC)
# include <mach/semaphore.h>
#endif
-#if defined(Q_OS_SYMBIAN)
-# include <e32std.h>
-#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 <errno.h>
#if defined(Q_OS_VXWORKS) && defined(wakeup)
#undef wakeup
#endif
-#if defined(Q_OS_MAC)
-# include <mach/mach.h>
-# include <mach/task.h>
-#elif defined(Q_OS_LINUX)
-# include <linux/futex.h>
-# include <sys/syscall.h>
-# include <unistd.h>
-# include <QtCore/qelapsedtimer.h>
-#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<int>("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;