QMutex is now just a pointer
authorOlivier Goffart <olivier.goffart@nokia.com>
Sat, 2 Jul 2011 13:13:12 +0000 (15:13 +0200)
committerQt by Nokia <qt-info@nokia.com>
Fri, 29 Jul 2011 08:32:07 +0000 (10:32 +0200)
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>
14 files changed:
src/corelib/kernel/qobject.cpp
src/corelib/thread/qmutex.cpp
src/corelib/thread/qmutex.h
src/corelib/thread/qmutex_linux.cpp [new file with mode: 0644]
src/corelib/thread/qmutex_mac.cpp [new file with mode: 0644]
src/corelib/thread/qmutex_p.h
src/corelib/thread/qmutex_unix.cpp
src/corelib/thread/qmutex_win.cpp
src/corelib/thread/qorderedmutexlocker_p.h
src/corelib/thread/qwaitcondition_unix.cpp
src/corelib/thread/qwaitcondition_win.cpp
src/corelib/thread/thread.pri
tests/auto/qmutex/tst_qmutex.cpp
tests/benchmarks/corelib/thread/qmutex/tst_qmutex.cpp

index 27edbda..ab9314c 100644 (file)
@@ -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;
 
index 3e3bf8f..c90b44b 100644 (file)
 #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
index cc66756..a49b981 100644 (file)
@@ -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 (file)
index 0000000..17015b8
--- /dev/null
@@ -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 (file)
index 0000000..f63feeb
--- /dev/null
@@ -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
index a9923c4..00f071e 100644 (file)
 #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
 
index 2a9d23c..0bccad5 100644 (file)
 #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
 
index 53ad8cf..f6670f6 100644 (file)
@@ -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()
index 8f01c1c..e65a601 100644 (file)
@@ -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();
index b946a9d..dd85bf4 100644 (file)
@@ -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;
     }
index 8a8db97..bf1ec5e 100644 (file)
@@ -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;
     }
index 592ab16..309bf30 100644 (file)
@@ -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 }
+}
index 5fed6bb..7ad6a98 100644 (file)
@@ -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"
index 05a1575..05e184d 100644 (file)
@@ -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;