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>
This commit is contained in:
parent
487583459e
commit
86a237929e
@ -878,7 +878,7 @@ QObject::~QObject()
|
|||||||
if (c->next) c->next->prev = c->prev;
|
if (c->next) c->next->prev = c->prev;
|
||||||
}
|
}
|
||||||
if (needToUnlock)
|
if (needToUnlock)
|
||||||
m->unlockInline();
|
m->unlock();
|
||||||
|
|
||||||
connectionList.first = c->nextConnectionList;
|
connectionList.first = c->nextConnectionList;
|
||||||
delete c;
|
delete c;
|
||||||
@ -902,7 +902,7 @@ QObject::~QObject()
|
|||||||
bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m);
|
bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m);
|
||||||
//the node has maybe been removed while the mutex was unlocked in relock?
|
//the node has maybe been removed while the mutex was unlocked in relock?
|
||||||
if (!node || node->sender != sender) {
|
if (!node || node->sender != sender) {
|
||||||
m->unlockInline();
|
m->unlock();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
node->receiver = 0;
|
node->receiver = 0;
|
||||||
@ -912,7 +912,7 @@ QObject::~QObject()
|
|||||||
|
|
||||||
node = node->next;
|
node = node->next;
|
||||||
if (needToUnlock)
|
if (needToUnlock)
|
||||||
m->unlockInline();
|
m->unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3076,7 +3076,7 @@ bool QMetaObjectPrivate::disconnectHelper(QObjectPrivate::Connection *c,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (needToUnlock)
|
if (needToUnlock)
|
||||||
receiverMutex->unlockInline();
|
receiverMutex->unlock();
|
||||||
|
|
||||||
c->receiver = 0;
|
c->receiver = 0;
|
||||||
|
|
||||||
|
@ -49,8 +49,25 @@
|
|||||||
#include "qthread.h"
|
#include "qthread.h"
|
||||||
#include "qmutex_p.h"
|
#include "qmutex_p.h"
|
||||||
|
|
||||||
|
#ifndef Q_OS_LINUX
|
||||||
|
#include "private/qfreelist_p.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
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
|
\class QMutex
|
||||||
\brief The QMutex class provides access serialization between threads.
|
\brief The QMutex class provides access serialization between threads.
|
||||||
@ -122,8 +139,12 @@ QT_BEGIN_NAMESPACE
|
|||||||
\sa lock(), unlock()
|
\sa lock(), unlock()
|
||||||
*/
|
*/
|
||||||
QMutex::QMutex(RecursionMode mode)
|
QMutex::QMutex(RecursionMode mode)
|
||||||
: d(new QMutexPrivate(mode))
|
{
|
||||||
{ }
|
if (mode == Recursive)
|
||||||
|
d = new QRecursiveMutexPrivate;
|
||||||
|
else
|
||||||
|
d = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Destroys the mutex.
|
Destroys the mutex.
|
||||||
@ -131,9 +152,18 @@ QMutex::QMutex(RecursionMode mode)
|
|||||||
\warning Destroying a locked mutex may result in undefined behavior.
|
\warning Destroying a locked mutex may result in undefined behavior.
|
||||||
*/
|
*/
|
||||||
QMutex::~QMutex()
|
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
|
Locks the mutex. If another thread has locked the mutex then this
|
||||||
call will block until that thread has unlocked it.
|
call will block until that thread has unlocked it.
|
||||||
|
|
||||||
@ -145,40 +175,8 @@ QMutex::~QMutex()
|
|||||||
|
|
||||||
\sa unlock()
|
\sa unlock()
|
||||||
*/
|
*/
|
||||||
void QMutex::lock()
|
|
||||||
{
|
|
||||||
QMutexPrivate *d = static_cast<QMutexPrivate *>(this->d);
|
|
||||||
Qt::HANDLE self;
|
|
||||||
|
|
||||||
if (d->recursive) {
|
/*!\fn bool QMutex::trylock()
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Attempts to lock the mutex. If the lock was obtained, this function
|
Attempts to lock the mutex. If the lock was obtained, this function
|
||||||
returns true. If another thread has locked the mutex, this
|
returns true. If another thread has locked the mutex, this
|
||||||
function returns false immediately.
|
function returns false immediately.
|
||||||
@ -195,36 +193,9 @@ void QMutex::lock()
|
|||||||
|
|
||||||
\sa lock(), unlock()
|
\sa lock(), unlock()
|
||||||
*/
|
*/
|
||||||
bool QMutex::tryLock()
|
|
||||||
{
|
|
||||||
QMutexPrivate *d = static_cast<QMutexPrivate *>(this->d);
|
|
||||||
Qt::HANDLE self;
|
|
||||||
|
|
||||||
if (d->recursive) {
|
/*! \fn bool QMutex::tryLock(int timeout)
|
||||||
self = QThread::currentThreadId();
|
\overload
|
||||||
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
|
|
||||||
|
|
||||||
Attempts to lock the mutex. This function returns true if the lock
|
Attempts to lock the mutex. This function returns true if the lock
|
||||||
was obtained; otherwise it returns false. If another thread has
|
was obtained; otherwise it returns false. If another thread has
|
||||||
@ -247,81 +218,30 @@ bool QMutex::tryLock()
|
|||||||
|
|
||||||
\sa lock(), unlock()
|
\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
|
Unlocks the mutex. Attempting to unlock a mutex in a different
|
||||||
thread to the one that locked it results in an error. Unlocking a
|
thread to the one that locked it results in an error. Unlocking a
|
||||||
mutex that is not locked results in undefined behavior.
|
mutex that is not locked results in undefined behavior.
|
||||||
|
|
||||||
\sa lock()
|
\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()
|
\fn void QMutex::isRecursive()
|
||||||
|
\since 5.0
|
||||||
|
|
||||||
Returns true if the mutex is locked by another thread; otherwise
|
Returns true if the mutex is recursive
|
||||||
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
|
|
||||||
*/
|
*/
|
||||||
|
bool QBasicMutex::isRecursive() {
|
||||||
|
QMutexPrivate *d = this->d;
|
||||||
|
if (quintptr(d) <= 0x3)
|
||||||
|
return false;
|
||||||
|
return d->recursive;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\class QMutexLocker
|
\class QMutexLocker
|
||||||
@ -418,96 +338,217 @@ void QMutex::unlock()
|
|||||||
\sa unlock()
|
\sa unlock()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef Q_OS_LINUX //linux implementation is in qmutex_linux.cpp
|
||||||
/*!
|
/*!
|
||||||
\fn QMutex::QMutex(bool recursive)
|
\internal helper for lock()
|
||||||
|
|
||||||
Use the constructor that takes a RecursionMode parameter instead.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\internal helper for lockInline()
|
|
||||||
*/
|
*/
|
||||||
void QMutex::lockInternal()
|
bool QBasicMutex::lockInternal(int timeout)
|
||||||
{
|
{
|
||||||
QMutexPrivate *d = static_cast<QMutexPrivate *>(this->d);
|
while (!fastTryLock()) {
|
||||||
|
QMutexPrivate *d = this->d;
|
||||||
|
if (!d) // if d is 0, the mutex is unlocked
|
||||||
|
continue;
|
||||||
|
|
||||||
if (QThread::idealThreadCount() == 1) {
|
if (d == dummyLocked()) {
|
||||||
// don't spin on single cpu machines
|
if (timeout == 0)
|
||||||
bool isLocked = d->wait();
|
return false;
|
||||||
Q_ASSERT_X(isLocked, "QMutex::lock",
|
QMutexPrivate *newD = QMutexPrivate::allocate();
|
||||||
"Internal error, infinite wait has timed out.");
|
if (!this->d.testAndSetOrdered(d, newD)) {
|
||||||
Q_UNUSED(isLocked);
|
//Either the mutex is already unlocked, or another thread already set it.
|
||||||
return;
|
newD->deref();
|
||||||
}
|
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));
|
|
||||||
}
|
}
|
||||||
|
d = newD;
|
||||||
// adjust the spin count when spinning does not benefit contention performance
|
//the d->refCount is already 1 the deref will occurs when we unlock
|
||||||
if ((spinTime + actualWaitTime) - qint64(QMutexPrivate::MaximumSpinTimeThreshold) >= qint64(QMutexPrivate::MaximumSpinTimeThreshold)) {
|
} else if (d->recursive) {
|
||||||
// long waits, stop spinning
|
return static_cast<QRecursiveMutexPrivate *>(d)->lock(timeout);
|
||||||
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));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// 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)
|
if (timeout == 0 && !d->possiblyUnlocked)
|
||||||
qint64 maximumSpinTime = d->maximumSpinTime;
|
return false;
|
||||||
qint64 spinTime = elapsedTimer.nsecsElapsed();
|
|
||||||
if (spinTime < maximumSpinTime / 2) {
|
if (!d->ref())
|
||||||
// we are using much less time than we need, adjust the limit
|
continue; //that QMutexPrivate was already released
|
||||||
d->maximumSpinTime = qBound(qint64(d->averageWaitTime * 3 / 2), maximumSpinTime / 2, qint64(QMutexPrivate::MaximumSpinTimeThreshold));
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Q_ASSERT(this->d);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\internal
|
\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();
|
||||||
|
}
|
||||||
|
|
||||||
|
//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
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\internal
|
||||||
|
*/
|
||||||
|
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::lockInline()
|
|
||||||
\internal
|
\internal
|
||||||
inline version of QMutex::lock()
|
*/
|
||||||
*/
|
void QRecursiveMutexPrivate::unlock()
|
||||||
|
{
|
||||||
/*!
|
if (count > 0) {
|
||||||
\fn QMutex::unlockInline()
|
count--;
|
||||||
\internal
|
} else {
|
||||||
inline version of QMutex::unlock()
|
owner = 0;
|
||||||
*/
|
mutex.unlock();
|
||||||
|
}
|
||||||
/*!
|
}
|
||||||
\fn QMutex::tryLockInline()
|
|
||||||
\internal
|
|
||||||
inline version of QMutex::tryLock()
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
@ -52,47 +52,64 @@ QT_BEGIN_NAMESPACE
|
|||||||
|
|
||||||
QT_MODULE(Core)
|
QT_MODULE(Core)
|
||||||
|
|
||||||
#ifndef QT_NO_THREAD
|
#if !defined(QT_NO_THREAD) && !defined(qdoc)
|
||||||
|
|
||||||
class QAtomicInt;
|
class QMutexPrivate;
|
||||||
class QMutexData;
|
|
||||||
|
|
||||||
class Q_CORE_EXPORT QMutex
|
class Q_CORE_EXPORT QBasicMutex
|
||||||
{
|
{
|
||||||
friend class QWaitCondition;
|
|
||||||
friend class QWaitConditionPrivate;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum RecursionMode { NonRecursive, Recursive };
|
inline void lock() {
|
||||||
|
if (!fastTryLock())
|
||||||
|
lockInternal();
|
||||||
|
}
|
||||||
|
|
||||||
explicit QMutex(RecursionMode mode = NonRecursive);
|
inline void unlock() {
|
||||||
~QMutex();
|
Q_ASSERT(d); //mutex must be locked
|
||||||
|
if (!d.testAndSetRelease(dummyLocked(), 0))
|
||||||
|
unlockInternal();
|
||||||
|
}
|
||||||
|
|
||||||
void lock(); //### Qt5: make inline;
|
bool tryLock(int timeout = 0) {
|
||||||
inline void lockInline();
|
return fastTryLock() || lockInternal(timeout);
|
||||||
bool tryLock(); //### Qt5: make inline;
|
}
|
||||||
bool tryLock(int timeout);
|
|
||||||
inline bool tryLockInline();
|
bool isRecursive();
|
||||||
void unlock(); //### Qt5: make inline;
|
|
||||||
inline void unlockInline();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void lockInternal();
|
inline bool fastTryLock() {
|
||||||
|
return d.testAndSetAcquire(0, dummyLocked());
|
||||||
|
}
|
||||||
|
bool lockInternal(int timeout = -1);
|
||||||
void unlockInternal();
|
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
|
class Q_CORE_EXPORT QMutexLocker
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
inline explicit QMutexLocker(QMutex *m)
|
inline explicit QMutexLocker(QBasicMutex *m)
|
||||||
{
|
{
|
||||||
Q_ASSERT_X((reinterpret_cast<quintptr>(m) & quintptr(1u)) == quintptr(0),
|
Q_ASSERT_X((reinterpret_cast<quintptr>(m) & quintptr(1u)) == quintptr(0),
|
||||||
"QMutexLocker", "QMutex pointer is misaligned");
|
"QMutexLocker", "QMutex pointer is misaligned");
|
||||||
if (m) {
|
if (m) {
|
||||||
m->lockInline();
|
m->lock();
|
||||||
val = reinterpret_cast<quintptr>(m) | quintptr(1u);
|
val = reinterpret_cast<quintptr>(m) | quintptr(1u);
|
||||||
} else {
|
} else {
|
||||||
val = 0;
|
val = 0;
|
||||||
@ -104,7 +121,7 @@ public:
|
|||||||
{
|
{
|
||||||
if ((val & quintptr(1u)) == quintptr(1u)) {
|
if ((val & quintptr(1u)) == quintptr(1u)) {
|
||||||
val &= ~quintptr(1u);
|
val &= ~quintptr(1u);
|
||||||
mutex()->unlockInline();
|
mutex()->unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,7 +129,7 @@ public:
|
|||||||
{
|
{
|
||||||
if (val) {
|
if (val) {
|
||||||
if ((val & quintptr(1u)) == quintptr(0u)) {
|
if ((val & quintptr(1u)) == quintptr(0u)) {
|
||||||
mutex()->lockInline();
|
mutex()->lock();
|
||||||
val |= quintptr(1u);
|
val |= quintptr(1u);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -138,54 +155,9 @@ private:
|
|||||||
quintptr val;
|
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
|
class Q_CORE_EXPORT QMutex
|
||||||
@ -194,14 +166,11 @@ public:
|
|||||||
enum RecursionMode { NonRecursive, Recursive };
|
enum RecursionMode { NonRecursive, Recursive };
|
||||||
|
|
||||||
inline explicit QMutex(RecursionMode mode = NonRecursive) { Q_UNUSED(mode); }
|
inline explicit QMutex(RecursionMode mode = NonRecursive) { Q_UNUSED(mode); }
|
||||||
inline ~QMutex() {}
|
|
||||||
|
|
||||||
static inline void lock() {}
|
static inline void lock() {}
|
||||||
static inline void lockInline() {}
|
|
||||||
static inline bool tryLock(int timeout = 0) { Q_UNUSED(timeout); return true; }
|
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 unlock() {}
|
||||||
static inline void unlockInline() {}
|
static inline bool isRecursive() { return true; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Q_DISABLE_COPY(QMutex)
|
Q_DISABLE_COPY(QMutex)
|
||||||
@ -221,7 +190,9 @@ private:
|
|||||||
Q_DISABLE_COPY(QMutexLocker)
|
Q_DISABLE_COPY(QMutexLocker)
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // QT_NO_THREAD
|
typedef QMutex QBasicMutex;
|
||||||
|
|
||||||
|
#endif // QT_NO_THREAD or qdoc
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
138
src/corelib/thread/qmutex_linux.cpp
Normal file
138
src/corelib/thread/qmutex_linux.cpp
Normal file
@ -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
|
96
src/corelib/thread/qmutex_mac.cpp
Normal file
96
src/corelib/thread/qmutex_mac.cpp
Normal file
@ -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
|
@ -57,50 +57,80 @@
|
|||||||
#include <QtCore/qglobal.h>
|
#include <QtCore/qglobal.h>
|
||||||
#include <QtCore/qnamespace.h>
|
#include <QtCore/qnamespace.h>
|
||||||
#include <QtCore/qmutex.h>
|
#include <QtCore/qmutex.h>
|
||||||
|
#include <QtCore/qatomic.h>
|
||||||
|
|
||||||
#if defined(Q_OS_MAC)
|
#if defined(Q_OS_MAC)
|
||||||
# include <mach/semaphore.h>
|
# include <mach/semaphore.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(Q_OS_SYMBIAN)
|
|
||||||
# include <e32std.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
class QMutexPrivate : public QMutexData {
|
class QMutexPrivate {
|
||||||
public:
|
public:
|
||||||
QMutexPrivate(QMutex::RecursionMode mode);
|
|
||||||
~QMutexPrivate();
|
~QMutexPrivate();
|
||||||
|
QMutexPrivate(QMutex::RecursionMode mode = QMutex::NonRecursive);
|
||||||
|
|
||||||
bool wait(int timeout = -1);
|
bool wait(int timeout = -1);
|
||||||
void wakeUp();
|
void wakeUp();
|
||||||
|
|
||||||
// 1ms = 1000000ns
|
#if !defined(Q_OS_LINUX)
|
||||||
enum { MaximumSpinTimeThreshold = 1000000 };
|
// Conrol the lifetime of the privates
|
||||||
volatile qint64 maximumSpinTime;
|
QAtomicInt refCount;
|
||||||
volatile qint64 averageWaitTime;
|
int id;
|
||||||
Qt::HANDLE owner;
|
|
||||||
uint count;
|
|
||||||
|
|
||||||
|
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)
|
#if defined(Q_OS_MAC)
|
||||||
semaphore_t mach_semaphore;
|
semaphore_t mach_semaphore;
|
||||||
#elif defined(Q_OS_UNIX) && !defined(Q_OS_LINUX) && !defined(Q_OS_SYMBIAN)
|
#elif defined(Q_OS_UNIX) && !defined(Q_OS_LINUX)
|
||||||
volatile bool wakeup;
|
bool wakeup;
|
||||||
pthread_mutex_t mutex;
|
pthread_mutex_t mutex;
|
||||||
pthread_cond_t cond;
|
pthread_cond_t cond;
|
||||||
#elif defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
|
#elif defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
|
||||||
HANDLE event;
|
HANDLE event;
|
||||||
#elif defined(Q_OS_SYMBIAN)
|
|
||||||
RSemaphore lock;
|
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
inline QMutexData::QMutexData(QMutex::RecursionMode mode)
|
class QRecursiveMutexPrivate : public QMutexPrivate
|
||||||
: recursive(mode == QMutex::Recursive)
|
{
|
||||||
{}
|
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
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
@ -46,142 +46,35 @@
|
|||||||
#ifndef QT_NO_THREAD
|
#ifndef QT_NO_THREAD
|
||||||
#include "qatomic.h"
|
#include "qatomic.h"
|
||||||
#include "qmutex_p.h"
|
#include "qmutex_p.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#if defined(Q_OS_VXWORKS) && defined(wakeup)
|
#if defined(Q_OS_VXWORKS) && defined(wakeup)
|
||||||
#undef wakeup
|
#undef wakeup
|
||||||
#endif
|
#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
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
#if !defined(Q_OS_MAC) && !defined(Q_OS_LINUX)
|
|
||||||
static void report_error(int code, const char *where, const char *what)
|
static void report_error(int code, const char *where, const char *what)
|
||||||
{
|
{
|
||||||
if (code != 0)
|
if (code != 0)
|
||||||
qWarning("%s: %s failure: %s", where, what, qPrintable(qt_error_string(code)));
|
qWarning("%s: %s failure: %s", where, what, qPrintable(qt_error_string(code)));
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
QMutexPrivate::QMutexPrivate(QMutex::RecursionMode mode)
|
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_mutex_init(&mutex, NULL), "QMutex", "mutex init");
|
||||||
report_error(pthread_cond_init(&cond, NULL), "QMutex", "cv init");
|
report_error(pthread_cond_init(&cond, NULL), "QMutex", "cv init");
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QMutexPrivate::~QMutexPrivate()
|
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_cond_destroy(&cond), "QMutex", "cv destroy");
|
||||||
report_error(pthread_mutex_destroy(&mutex), "QMutex", "mutex 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)
|
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");
|
report_error(pthread_mutex_lock(&mutex), "QMutex::lock", "mutex lock");
|
||||||
int errorCode = 0;
|
int errorCode = 0;
|
||||||
while (!wakeup) {
|
while (!wakeup) {
|
||||||
@ -190,12 +83,10 @@ bool QMutexPrivate::wait(int timeout)
|
|||||||
} else {
|
} else {
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
gettimeofday(&tv, 0);
|
gettimeofday(&tv, 0);
|
||||||
|
|
||||||
timespec ti;
|
timespec ti;
|
||||||
ti.tv_nsec = (tv.tv_usec + (timeout % 1000) * 1000) * 1000;
|
ti.tv_nsec = (tv.tv_usec + (timeout % 1000) * 1000) * 1000;
|
||||||
ti.tv_sec = tv.tv_sec + (timeout / 1000) + (ti.tv_nsec / 1000000000);
|
ti.tv_sec = tv.tv_sec + (timeout / 1000) + (ti.tv_nsec / 1000000000);
|
||||||
ti.tv_nsec %= 1000000000;
|
ti.tv_nsec %= 1000000000;
|
||||||
|
|
||||||
errorCode = pthread_cond_timedwait(&cond, &mutex, &ti);
|
errorCode = pthread_cond_timedwait(&cond, &mutex, &ti);
|
||||||
}
|
}
|
||||||
if (errorCode) {
|
if (errorCode) {
|
||||||
@ -207,10 +98,10 @@ bool QMutexPrivate::wait(int timeout)
|
|||||||
report_error(errorCode, "QMutex::lock()", "cv wait");
|
report_error(errorCode, "QMutex::lock()", "cv wait");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
bool ret = wakeup;
|
||||||
wakeup = false;
|
wakeup = false;
|
||||||
report_error(pthread_mutex_unlock(&mutex), "QMutex::lock", "mutex unlock");
|
report_error(pthread_mutex_unlock(&mutex), "QMutex::lock", "mutex unlock");
|
||||||
contenders.deref();
|
return ret;
|
||||||
return errorCode == 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QMutexPrivate::wakeUp()
|
void QMutexPrivate::wakeUp()
|
||||||
@ -221,7 +112,6 @@ void QMutexPrivate::wakeUp()
|
|||||||
report_error(pthread_mutex_unlock(&mutex), "QMutex::unlock", "mutex unlock");
|
report_error(pthread_mutex_unlock(&mutex), "QMutex::unlock", "mutex unlock");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // !Q_OS_MAC && !Q_OS_LINUX
|
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@
|
|||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
QMutexPrivate::QMutexPrivate(QMutex::RecursionMode mode)
|
QMutexPrivate::QMutexPrivate(QMutex::RecursionMode mode)
|
||||||
: QMutexData(mode), maximumSpinTime(MaximumSpinTimeThreshold), averageWaitTime(0), owner(0), count(0)
|
: recursive(mode)
|
||||||
{
|
{
|
||||||
event = CreateEvent(0, FALSE, FALSE, 0);
|
event = CreateEvent(0, FALSE, FALSE, 0);
|
||||||
if (!event)
|
if (!event)
|
||||||
@ -60,13 +60,7 @@ QMutexPrivate::~QMutexPrivate()
|
|||||||
|
|
||||||
bool QMutexPrivate::wait(int timeout)
|
bool QMutexPrivate::wait(int timeout)
|
||||||
{
|
{
|
||||||
if (contenders.fetchAndAddAcquire(1) == 0) {
|
return (WaitForSingleObject(event, timeout < 0 ? INFINITE : timeout) == WAIT_OBJECT_0);
|
||||||
// lock acquired without waiting
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool returnValue = (WaitForSingleObject(event, timeout < 0 ? INFINITE : timeout) == WAIT_OBJECT_0);
|
|
||||||
contenders.deref();
|
|
||||||
return returnValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QMutexPrivate::wakeUp()
|
void QMutexPrivate::wakeUp()
|
||||||
|
@ -79,8 +79,8 @@ public:
|
|||||||
void relock()
|
void relock()
|
||||||
{
|
{
|
||||||
if (!locked) {
|
if (!locked) {
|
||||||
if (mtx1) mtx1->lockInline();
|
if (mtx1) mtx1->lock();
|
||||||
if (mtx2) mtx2->lockInline();
|
if (mtx2) mtx2->lock();
|
||||||
locked = true;
|
locked = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,8 +88,8 @@ public:
|
|||||||
void unlock()
|
void unlock()
|
||||||
{
|
{
|
||||||
if (locked) {
|
if (locked) {
|
||||||
if (mtx1) mtx1->unlockInline();
|
if (mtx1) mtx1->unlock();
|
||||||
if (mtx2) mtx2->unlockInline();
|
if (mtx2) mtx2->unlock();
|
||||||
locked = false;
|
locked = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -100,10 +100,10 @@ public:
|
|||||||
if (mtx1 == mtx2)
|
if (mtx1 == mtx2)
|
||||||
return false;
|
return false;
|
||||||
if (mtx1 < mtx2) {
|
if (mtx1 < mtx2) {
|
||||||
mtx2->lockInline();
|
mtx2->lock();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!mtx2->tryLockInline()) {
|
if (!mtx2->tryLock()) {
|
||||||
mtx1->unlock();
|
mtx1->unlock();
|
||||||
mtx2->lock();
|
mtx2->lock();
|
||||||
mtx1->lock();
|
mtx1->lock();
|
||||||
|
@ -146,7 +146,7 @@ bool QWaitCondition::wait(QMutex *mutex, unsigned long time)
|
|||||||
{
|
{
|
||||||
if (! mutex)
|
if (! mutex)
|
||||||
return false;
|
return false;
|
||||||
if (mutex->d->recursive) {
|
if (mutex->isRecursive()) {
|
||||||
qWarning("QWaitCondition: cannot wait on recursive mutexes");
|
qWarning("QWaitCondition: cannot wait on recursive mutexes");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -164,7 +164,7 @@ bool QWaitCondition::wait(QMutex *mutex, unsigned long time)
|
|||||||
{
|
{
|
||||||
if (!mutex)
|
if (!mutex)
|
||||||
return false;
|
return false;
|
||||||
if (mutex->d->recursive) {
|
if (mutex->isRecursive()) {
|
||||||
qWarning("QWaitCondition::wait: Cannot wait on recursive mutexes");
|
qWarning("QWaitCondition::wait: Cannot wait on recursive mutexes");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -24,8 +24,7 @@ SOURCES += thread/qatomic.cpp \
|
|||||||
thread/qthread.cpp \
|
thread/qthread.cpp \
|
||||||
thread/qthreadstorage.cpp
|
thread/qthreadstorage.cpp
|
||||||
|
|
||||||
unix:!symbian:SOURCES += thread/qmutex_unix.cpp \
|
unix:!symbian:SOURCES += thread/qthread_unix.cpp \
|
||||||
thread/qthread_unix.cpp \
|
|
||||||
thread/qwaitcondition_unix.cpp
|
thread/qwaitcondition_unix.cpp
|
||||||
|
|
||||||
symbian:SOURCES += thread/qmutex_symbian.cpp \
|
symbian:SOURCES += thread/qmutex_symbian.cpp \
|
||||||
@ -39,3 +38,9 @@ win32:SOURCES += thread/qmutex_win.cpp \
|
|||||||
integrity:SOURCES += thread/qmutex_unix.cpp \
|
integrity:SOURCES += thread/qmutex_unix.cpp \
|
||||||
thread/qthread_unix.cpp \
|
thread/qthread_unix.cpp \
|
||||||
thread/qwaitcondition_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 }
|
||||||
|
}
|
||||||
|
@ -65,6 +65,7 @@ private slots:
|
|||||||
void stressTest();
|
void stressTest();
|
||||||
void tryLockRace();
|
void tryLockRace();
|
||||||
void qtbug16115_trylock();
|
void qtbug16115_trylock();
|
||||||
|
void moreStress();
|
||||||
};
|
};
|
||||||
|
|
||||||
static const int iterations = 100;
|
static const int iterations = 100;
|
||||||
@ -83,6 +84,8 @@ QMutex normalMutex, recursiveMutex(QMutex::Recursive);
|
|||||||
QSemaphore testsTurn;
|
QSemaphore testsTurn;
|
||||||
QSemaphore threadsTurn;
|
QSemaphore threadsTurn;
|
||||||
|
|
||||||
|
enum { waitTime = 100 };
|
||||||
|
|
||||||
void tst_QMutex::tryLock()
|
void tst_QMutex::tryLock()
|
||||||
{
|
{
|
||||||
// test non-recursive mutex
|
// test non-recursive mutex
|
||||||
@ -109,18 +112,18 @@ void tst_QMutex::tryLock()
|
|||||||
threadsTurn.acquire();
|
threadsTurn.acquire();
|
||||||
QTime timer;
|
QTime timer;
|
||||||
timer.start();
|
timer.start();
|
||||||
QVERIFY(!normalMutex.tryLock(1000));
|
QVERIFY(!normalMutex.tryLock(waitTime));
|
||||||
QVERIFY(timer.elapsed() >= 1000);
|
QVERIFY(timer.elapsed() >= waitTime);
|
||||||
testsTurn.release();
|
testsTurn.release();
|
||||||
|
|
||||||
threadsTurn.acquire();
|
threadsTurn.acquire();
|
||||||
timer.start();
|
timer.start();
|
||||||
QVERIFY(normalMutex.tryLock(1000));
|
QVERIFY(normalMutex.tryLock(waitTime));
|
||||||
QVERIFY(timer.elapsed() <= 1000);
|
QVERIFY(timer.elapsed() <= waitTime);
|
||||||
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
timer.start();
|
timer.start();
|
||||||
QVERIFY(!normalMutex.tryLock(1000));
|
QVERIFY(!normalMutex.tryLock(waitTime));
|
||||||
QVERIFY(timer.elapsed() >= 1000);
|
QVERIFY(timer.elapsed() >= waitTime);
|
||||||
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
||||||
normalMutex.unlock();
|
normalMutex.unlock();
|
||||||
testsTurn.release();
|
testsTurn.release();
|
||||||
@ -132,7 +135,7 @@ void tst_QMutex::tryLock()
|
|||||||
threadsTurn.acquire();
|
threadsTurn.acquire();
|
||||||
timer.start();
|
timer.start();
|
||||||
QVERIFY(normalMutex.tryLock(0));
|
QVERIFY(normalMutex.tryLock(0));
|
||||||
QVERIFY(timer.elapsed() < 1000);
|
QVERIFY(timer.elapsed() < waitTime);
|
||||||
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
QVERIFY(!normalMutex.tryLock(0));
|
QVERIFY(!normalMutex.tryLock(0));
|
||||||
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
||||||
@ -158,13 +161,13 @@ void tst_QMutex::tryLock()
|
|||||||
normalMutex.unlock();
|
normalMutex.unlock();
|
||||||
threadsTurn.release();
|
threadsTurn.release();
|
||||||
|
|
||||||
// thread can't acquire lock, timeout = 1000
|
// thread can't acquire lock, timeout = waitTime
|
||||||
testsTurn.acquire();
|
testsTurn.acquire();
|
||||||
normalMutex.lock();
|
normalMutex.lock();
|
||||||
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
threadsTurn.release();
|
threadsTurn.release();
|
||||||
|
|
||||||
// thread can acquire lock, timeout = 1000
|
// thread can acquire lock, timeout = waitTime
|
||||||
testsTurn.acquire();
|
testsTurn.acquire();
|
||||||
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
||||||
normalMutex.unlock();
|
normalMutex.unlock();
|
||||||
@ -215,17 +218,17 @@ void tst_QMutex::tryLock()
|
|||||||
threadsTurn.acquire();
|
threadsTurn.acquire();
|
||||||
QTime timer;
|
QTime timer;
|
||||||
timer.start();
|
timer.start();
|
||||||
QVERIFY(!recursiveMutex.tryLock(1000));
|
QVERIFY(!recursiveMutex.tryLock(waitTime));
|
||||||
QVERIFY(timer.elapsed() >= 1000);
|
QVERIFY(timer.elapsed() >= waitTime);
|
||||||
QVERIFY(!recursiveMutex.tryLock(0));
|
QVERIFY(!recursiveMutex.tryLock(0));
|
||||||
testsTurn.release();
|
testsTurn.release();
|
||||||
|
|
||||||
threadsTurn.acquire();
|
threadsTurn.acquire();
|
||||||
timer.start();
|
timer.start();
|
||||||
QVERIFY(recursiveMutex.tryLock(1000));
|
QVERIFY(recursiveMutex.tryLock(waitTime));
|
||||||
QVERIFY(timer.elapsed() <= 1000);
|
QVERIFY(timer.elapsed() <= waitTime);
|
||||||
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
QVERIFY(recursiveMutex.tryLock(1000));
|
QVERIFY(recursiveMutex.tryLock(waitTime));
|
||||||
QVERIFY(lockCount.testAndSetRelaxed(1, 2));
|
QVERIFY(lockCount.testAndSetRelaxed(1, 2));
|
||||||
QVERIFY(lockCount.testAndSetRelaxed(2, 1));
|
QVERIFY(lockCount.testAndSetRelaxed(2, 1));
|
||||||
recursiveMutex.unlock();
|
recursiveMutex.unlock();
|
||||||
@ -241,7 +244,7 @@ void tst_QMutex::tryLock()
|
|||||||
threadsTurn.acquire();
|
threadsTurn.acquire();
|
||||||
timer.start();
|
timer.start();
|
||||||
QVERIFY(recursiveMutex.tryLock(0));
|
QVERIFY(recursiveMutex.tryLock(0));
|
||||||
QVERIFY(timer.elapsed() < 1000);
|
QVERIFY(timer.elapsed() < waitTime);
|
||||||
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
QVERIFY(recursiveMutex.tryLock(0));
|
QVERIFY(recursiveMutex.tryLock(0));
|
||||||
QVERIFY(lockCount.testAndSetRelaxed(1, 2));
|
QVERIFY(lockCount.testAndSetRelaxed(1, 2));
|
||||||
@ -274,7 +277,7 @@ void tst_QMutex::tryLock()
|
|||||||
recursiveMutex.unlock();
|
recursiveMutex.unlock();
|
||||||
threadsTurn.release();
|
threadsTurn.release();
|
||||||
|
|
||||||
// thread can't acquire lock, timeout = 1000
|
// thread can't acquire lock, timeout = waitTime
|
||||||
testsTurn.acquire();
|
testsTurn.acquire();
|
||||||
recursiveMutex.lock();
|
recursiveMutex.lock();
|
||||||
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
@ -282,7 +285,7 @@ void tst_QMutex::tryLock()
|
|||||||
QVERIFY(lockCount.testAndSetRelaxed(1, 2));
|
QVERIFY(lockCount.testAndSetRelaxed(1, 2));
|
||||||
threadsTurn.release();
|
threadsTurn.release();
|
||||||
|
|
||||||
// thread can acquire lock, timeout = 1000
|
// thread can acquire lock, timeout = waitTime
|
||||||
testsTurn.acquire();
|
testsTurn.acquire();
|
||||||
QVERIFY(lockCount.testAndSetRelaxed(2, 1));
|
QVERIFY(lockCount.testAndSetRelaxed(2, 1));
|
||||||
recursiveMutex.unlock();
|
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
|
class StressTestThread : public QThread
|
||||||
{
|
{
|
||||||
@ -497,7 +501,7 @@ public:
|
|||||||
do {
|
do {
|
||||||
if (mutex.tryLock())
|
if (mutex.tryLock())
|
||||||
mutex.unlock();
|
mutex.unlock();
|
||||||
} while (t.elapsed() < 20000);
|
} while (t.elapsed() < one_minute/2);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
QMutex TryLockRaceThread::mutex;
|
QMutex TryLockRaceThread::mutex;
|
||||||
@ -534,7 +538,7 @@ void tst_QMutex::qtbug16115_trylock()
|
|||||||
TrylockThread(QMutex &mut) : mut(mut) {}
|
TrylockThread(QMutex &mut) : mut(mut) {}
|
||||||
QMutex &mut;
|
QMutex &mut;
|
||||||
void run() {
|
void run() {
|
||||||
for (int i = 0; i < 1000000; ++i) {
|
for (int i = 0; i < 100000; ++i) {
|
||||||
if (mut.tryLock(0)) {
|
if (mut.tryLock(0)) {
|
||||||
if ((++qtbug16115_trylock_counter) != 1)
|
if ((++qtbug16115_trylock_counter) != 1)
|
||||||
++qtbug16115_failure_count;
|
++qtbug16115_failure_count;
|
||||||
@ -553,7 +557,7 @@ void tst_QMutex::qtbug16115_trylock()
|
|||||||
t2.start();
|
t2.start();
|
||||||
t3.start();
|
t3.start();
|
||||||
|
|
||||||
for (int i = 0; i < 1000000; ++i) {
|
for (int i = 0; i < 100000; ++i) {
|
||||||
mut.lock();
|
mut.lock();
|
||||||
if ((++qtbug16115_trylock_counter) != 1)
|
if ((++qtbug16115_trylock_counter) != 1)
|
||||||
++qtbug16115_failure_count;
|
++qtbug16115_failure_count;
|
||||||
@ -567,5 +571,70 @@ void tst_QMutex::qtbug16115_trylock()
|
|||||||
QCOMPARE(qtbug16115_failure_count, 0);
|
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)
|
QTEST_MAIN(tst_QMutex)
|
||||||
#include "tst_qmutex.moc"
|
#include "tst_qmutex.moc"
|
||||||
|
@ -151,7 +151,6 @@ void tst_QMutex::noThread_data()
|
|||||||
QTest::addColumn<int>("t");
|
QTest::addColumn<int>("t");
|
||||||
|
|
||||||
QTest::newRow("noLock") << 1;
|
QTest::newRow("noLock") << 1;
|
||||||
QTest::newRow("QMutexInline") << 2;
|
|
||||||
QTest::newRow("QMutex") << 3;
|
QTest::newRow("QMutex") << 3;
|
||||||
QTest::newRow("QMutexLocker") << 4;
|
QTest::newRow("QMutexLocker") << 4;
|
||||||
}
|
}
|
||||||
@ -172,16 +171,6 @@ void tst_QMutex::noThread()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 2:
|
|
||||||
QBENCHMARK {
|
|
||||||
count = 0;
|
|
||||||
for (int i = 0; i < N; i++) {
|
|
||||||
mtx.lockInline();
|
|
||||||
count++;
|
|
||||||
mtx.unlockInline();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 3:
|
case 3:
|
||||||
QBENCHMARK {
|
QBENCHMARK {
|
||||||
count = 0;
|
count = 0;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user