Split QMutex and QRecursiveMutex

These classes should not inherit from each other
anymore in Qt 6. The reason is that this makes
the 95% case of using a non-recursive mutex
much slower than it has to be.

This way, QMutex can now inline the fast path
and be pretty much as fast as QBasicMutex is
in Qt 5. They actually use the same code paths
now. The main difference is that QMutex allows
calling tryLock() with a timeout, which that
is not allowed for QBasicMutex.

[ChangeLog][QtCore][QMutex] QMutex does not support
recursive locking anymore. Use QRecursiveMutex for that
purpose. QRecursiveMutex does not inherit QMutex anymore
in Qt 6.

Change-Id: I10f9bab6269a9181a2e9f534fb72ce65bc76d989
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Lars Knoll 2020-09-04 15:35:16 +02:00
parent 77d812683f
commit d4b206b246
8 changed files with 289 additions and 294 deletions

View File

@ -53,24 +53,11 @@
QT_BEGIN_NAMESPACE
static inline bool isRecursive(QMutexData *d)
{
quintptr u = quintptr(d);
if (Q_LIKELY(u <= 0x3))
return false;
#ifdef QT_LINUX_FUTEX
Q_ASSERT(d->recursive);
return true;
#else
return d->recursive;
#endif
}
class QRecursiveMutexPrivate : public QMutexData
class QRecursiveMutexPrivate
{
public:
QRecursiveMutexPrivate()
: QMutexData(QMutex::Recursive), owner(nullptr), count(0) {}
: owner(nullptr), count(0) {}
// written to by the thread that first owns 'mutex';
// read during attempts to acquire ownership of 'mutex' from any other thread:
@ -142,75 +129,37 @@ public:
lock calls unlock(). A non-blocking alternative to lock() is
tryLock().
QMutex is optimized to be fast in the non-contended case. A non-recursive
QMutex will not allocate memory if there is no contention on that mutex.
QMutex is optimized to be fast in the non-contended case. It
will not allocate memory if there is no contention on that mutex.
It is constructed and destroyed with almost no overhead,
which means it is fine to have many mutexes as part of other classes.
\sa QRecursiveMutex, QMutexLocker, QReadWriteLock, QSemaphore, QWaitCondition
*/
/*!
\enum QMutex::RecursionMode
\obsolete Use QRecursiveMutex to create a recursive mutex.
\value Recursive In this mode, a thread can lock the same mutex
multiple times and the mutex won't be unlocked
until a corresponding number of unlock() calls
have been made. You should use QRecursiveMutex
for this use-case.
\value NonRecursive In this mode, a thread may only lock a mutex
once.
\sa QMutex(), QRecursiveMutex
*/
/*!
\fn QMutex::QMutex()
Constructs a new mutex. The mutex is created in an unlocked state.
*/
/*!
Constructs a new mutex. The mutex is created in an unlocked state.
\obsolete Use QRecursiveMutex to create a recursive mutex.
/*! \fn QMutex::~QMutex()
If \a mode is QMutex::Recursive, a thread can lock the same mutex
multiple times and the mutex won't be unlocked until a
corresponding number of unlock() calls have been made. Otherwise
a thread may only lock a mutex once. The default is
QMutex::NonRecursive.
Recursive mutexes are slower and take more memory than non-recursive ones.
\sa lock(), unlock()
*/
QMutex::QMutex(RecursionMode mode)
{
d_ptr.storeRelaxed(mode == Recursive ? new QRecursiveMutexPrivate : nullptr);
}
/*!
Destroys the mutex.
\warning Destroying a locked mutex may result in undefined behavior.
*/
QMutex::~QMutex()
void QBasicMutex::destroyInternal(QMutexPrivate *d)
{
QMutexData *d = d_ptr.loadRelaxed();
if (QBasicMutex::isRecursive()) {
delete static_cast<QRecursiveMutexPrivate *>(d);
} else if (d) {
if (!d)
return;
#ifndef QT_LINUX_FUTEX
if (d != dummyLocked() && static_cast<QMutexPrivate *>(d)->possiblyUnlocked.loadRelaxed()
&& tryLock()) {
unlock();
return;
}
#endif
qWarning("QMutex: destroying locked mutex");
if (d != dummyLocked() && d->possiblyUnlocked.loadRelaxed() && tryLock()) {
unlock();
return;
}
#endif
qWarning("QMutex: destroying locked mutex");
}
/*! \fn void QMutex::lock()
@ -219,23 +168,10 @@ QMutex::~QMutex()
call will block until that thread has unlocked it.
Calling this function multiple times on the same mutex from the
same thread is allowed if this mutex is a
\l{QRecursiveMutex}{recursive mutex}. If this mutex is a
\l{QMutex}{non-recursive mutex}, this function will
\e dead-lock when the mutex is locked recursively.
same thread will cause a \e dead-lock.
\sa unlock()
*/
void QMutex::lock() QT_MUTEX_LOCK_NOEXCEPT
{
QMutexData *current;
if (fastTryLock(current))
return;
if (QT_PREPEND_NAMESPACE(isRecursive)(current))
static_cast<QRecursiveMutexPrivate *>(current)->lock(-1);
else
lockInternal();
}
/*! \fn bool QMutex::tryLock(int timeout)
@ -252,24 +188,25 @@ void QMutex::lock() QT_MUTEX_LOCK_NOEXCEPT
before another thread can successfully lock it.
Calling this function multiple times on the same mutex from the
same thread is allowed if this mutex is a
\l{QRecursiveMutex}{recursive mutex}. If this mutex is a
\l{QMutex}{non-recursive mutex}, this function will
\e always return false when attempting to lock the mutex
recursively.
same thread will cause a \e dead-lock.
\sa lock(), unlock()
*/
/*! \fn bool QMutex::tryLock()
\overload
Attempts to lock the mutex. This function returns \c true if the lock
was obtained; otherwise it returns \c false.
If the lock was obtained, the mutex must be unlocked with unlock()
before another thread can successfully lock it.
Calling this function multiple times on the same mutex from the
same thread will cause a \e dead-lock.
\sa lock(), unlock()
*/
bool QMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT
{
QMutexData *current;
if (fastTryLock(current))
return true;
if (QT_PREPEND_NAMESPACE(isRecursive)(current))
return static_cast<QRecursiveMutexPrivate *>(current)->lock(timeout);
else
return lockInternal(timeout);
}
/*! \fn bool QMutex::try_lock()
\since 5.8
@ -279,9 +216,6 @@ bool QMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT
This function is provided for compatibility with the Standard Library
concept \c Lockable. It is equivalent to tryLock().
The function returns \c true if the lock was obtained; otherwise it
returns \c false
*/
/*! \fn template <class Rep, class Period> bool QMutex::try_lock_for(std::chrono::duration<Rep, Period> duration)
@ -299,11 +233,7 @@ bool QMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT
before another thread can successfully lock it.
Calling this function multiple times on the same mutex from the
same thread is allowed if this mutex is a
\l{QRecursiveMutex}{recursive mutex}. If this mutex is a
\l{QMutex}{non-recursive mutex}, this function will
\e always return false when attempting to lock the mutex
recursively.
same thread will cause a \e dead-lock.
\sa lock(), unlock()
*/
@ -323,11 +253,7 @@ bool QMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT
before another thread can successfully lock it.
Calling this function multiple times on the same mutex from the
same thread is allowed if this mutex is a
\l{QRecursiveMutex}{recursive mutex}. If this mutex is a
\l{QMutex}{non-recursive mutex}, this function will
\e always return false when attempting to lock the mutex
recursively.
same thread will cause a \e dead-lock.
\sa lock(), unlock()
*/
@ -340,26 +266,6 @@ bool QMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT
\sa lock()
*/
void QMutex::unlock() noexcept
{
QMutexData *current;
if (fastTryUnlock(current))
return;
if (QT_PREPEND_NAMESPACE(isRecursive)(current))
static_cast<QRecursiveMutexPrivate *>(current)->unlock();
else
unlockInternal();
}
/*!
\since 5.7
Returns \c true if the mutex is recursive.
*/
bool QBasicMutex::isRecursive() const noexcept
{
return QT_PREPEND_NAMESPACE(isRecursive)(d_ptr.loadAcquire());
}
/*!
\class QRecursiveMutex
@ -398,9 +304,8 @@ bool QBasicMutex::isRecursive() const noexcept
\sa lock(), unlock()
*/
QRecursiveMutex::QRecursiveMutex()
: QMutex()
{
d_ptr.storeRelaxed(new QRecursiveMutexPrivate);
d = new QRecursiveMutexPrivate;
}
/*!
@ -410,9 +315,112 @@ QRecursiveMutex::QRecursiveMutex()
*/
QRecursiveMutex::~QRecursiveMutex()
{
delete static_cast<QRecursiveMutexPrivate*>(d_ptr.fetchAndStoreAcquire(nullptr));
delete d;
}
/*! \fn void QRecursiveMutex::lock()
Locks the mutex. If another thread has locked the mutex then this
call will block until that thread has unlocked it.
Calling this function multiple times on the same mutex from the
same thread is allowed.
\sa unlock()
*/
void QRecursiveMutex::lock() QT_MUTEX_LOCK_NOEXCEPT
{
d->lock(-1);
}
/*! \fn bool QMutex::tryLock(int timeout)
Attempts to lock the mutex. This function returns \c true if the lock
was obtained; otherwise it returns \c false. If another thread has
locked the mutex, this function will wait for at most \a timeout
milliseconds for the mutex to become available.
Note: Passing a negative number as the \a timeout is equivalent to
calling lock(), i.e. this function will wait forever until mutex
can be locked if \a timeout is negative.
If the lock was obtained, the mutex must be unlocked with unlock()
before another thread can successfully lock it.
Calling this function multiple times on the same mutex from the
same thread is allowed.
\sa lock(), unlock()
*/
bool QRecursiveMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT
{
return d->lock(timeout);
}
/*! \fn bool QRecursiveMutex::try_lock()
\since 5.8
Attempts to lock the mutex. This function returns \c true if the lock
was obtained; otherwise it returns \c false.
This function is provided for compatibility with the Standard Library
concept \c Lockable. It is equivalent to tryLock().
*/
/*! \fn template <class Rep, class Period> bool QRecursiveMutex::try_lock_for(std::chrono::duration<Rep, Period> duration)
\since 5.8
Attempts to lock the mutex. This function returns \c true if the lock
was obtained; otherwise it returns \c false. If another thread has
locked the mutex, this function will wait for at least \a duration
for the mutex to become available.
Note: Passing a negative duration as the \a duration is equivalent to
calling try_lock(). This behavior differs from tryLock().
If the lock was obtained, the mutex must be unlocked with unlock()
before another thread can successfully lock it.
Calling this function multiple times on the same mutex from the
same thread is allowed.
\sa lock(), unlock()
*/
/*! \fn template<class Clock, class Duration> bool QRecursiveMutex::try_lock_until(std::chrono::time_point<Clock, Duration> timePoint)
\since 5.8
Attempts to lock the mutex. This function returns \c true if the lock
was obtained; otherwise it returns \c false. If another thread has
locked the mutex, this function will wait at least until \a timePoint
for the mutex to become available.
Note: Passing a \a timePoint which has already passed is equivalent
to calling try_lock(). This behavior differs from tryLock().
If the lock was obtained, the mutex must be unlocked with unlock()
before another thread can successfully lock it.
Calling this function multiple times on the same mutex from the
same thread is allowed.
\sa lock(), unlock()
*/
/*! \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 QRecursiveMutex::unlock() noexcept
{
d->unlock();
}
/*!
\class QMutexLocker
\inmodule QtCore
@ -555,10 +563,8 @@ void QBasicMutex::lockInternal() QT_MUTEX_LOCK_NOEXCEPT
*/
bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT
{
Q_ASSERT(!isRecursive());
while (!fastTryLock()) {
QMutexData *copy = d_ptr.loadAcquire();
QMutexPrivate *copy = d_ptr.loadAcquire();
if (!copy) // if d is 0, the mutex is unlocked
continue;
@ -586,7 +592,7 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT
// We will try to reference it to avoid unlock to release it to the pool to make
// sure it won't be released. But if the refcount is already 0 it has been released.
if (!d->ref())
continue; //that QMutexData was already released
continue; //that QMutexPrivate was already released
// We now hold a reference to the QMutexPrivate. It won't be released and re-used.
// But it is still possible that it was already re-used by another QMutex right before
@ -663,10 +669,9 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT
*/
void QBasicMutex::unlockInternal() noexcept
{
QMutexData *copy = d_ptr.loadAcquire();
QMutexPrivate *copy = d_ptr.loadAcquire();
Q_ASSERT(copy); //we must be locked
Q_ASSERT(copy != dummyLocked()); // testAndSetRelease(dummyLocked(), 0) failed
Q_ASSERT(!isRecursive());
QMutexPrivate *d = reinterpret_cast<QMutexPrivate *>(copy);
@ -719,7 +724,6 @@ QMutexPrivate *QMutexPrivate::allocate()
QMutexPrivate *d = &(*freelist())[i];
d->id = i;
Q_ASSERT(d->refCount.loadRelaxed() == 0);
Q_ASSERT(!d->recursive);
Q_ASSERT(!d->possiblyUnlocked.loadRelaxed());
Q_ASSERT(d->waiters.loadRelaxed() == 0);
d->refCount.storeRelaxed(1);
@ -728,7 +732,6 @@ QMutexPrivate *QMutexPrivate::allocate()
void QMutexPrivate::release()
{
Q_ASSERT(!recursive);
Q_ASSERT(refCount.loadRelaxed() == 0);
Q_ASSERT(!possiblyUnlocked.loadRelaxed());
Q_ASSERT(waiters.loadRelaxed() == 0);
@ -764,7 +767,7 @@ inline bool QRecursiveMutexPrivate::lock(int timeout) QT_MUTEX_LOCK_NOEXCEPT
}
bool success = true;
if (timeout == -1) {
mutex.QBasicMutex::lock();
mutex.lock();
} else {
success = mutex.tryLock(timeout);
}
@ -783,7 +786,7 @@ inline void QRecursiveMutexPrivate::unlock() noexcept
count--;
} else {
owner.storeRelaxed(nullptr);
mutex.QBasicMutex::unlock();
mutex.unlock();
}
}

View File

@ -64,122 +64,11 @@ QT_BEGIN_NAMESPACE
class QMutex;
class QRecursiveMutex;
class QMutexData;
class Q_CORE_EXPORT QBasicMutex
{
public:
#ifdef Q_COMPILER_CONSTEXPR
constexpr QBasicMutex()
: d_ptr(nullptr)
{}
#endif
// BasicLockable concept
inline void lock() QT_MUTEX_LOCK_NOEXCEPT {
if (!fastTryLock())
lockInternal();
}
// BasicLockable concept
inline void unlock() noexcept {
Q_ASSERT(d_ptr.loadRelaxed()); //mutex must be locked
if (!fastTryUnlock())
unlockInternal();
}
bool tryLock() noexcept {
return fastTryLock();
}
// Lockable concept
bool try_lock() noexcept { return tryLock(); }
bool isRecursive() const noexcept;
private:
inline bool fastTryLock() noexcept {
return d_ptr.testAndSetAcquire(nullptr, dummyLocked());
}
inline bool fastTryUnlock() noexcept {
return d_ptr.testAndSetRelease(dummyLocked(), nullptr);
}
inline bool fastTryLock(QMutexData *&current) noexcept {
return d_ptr.testAndSetAcquire(nullptr, dummyLocked(), current);
}
inline bool fastTryUnlock(QMutexData *&current) noexcept {
return d_ptr.testAndSetRelease(dummyLocked(), nullptr, current);
}
void lockInternal() QT_MUTEX_LOCK_NOEXCEPT;
bool lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT;
void unlockInternal() noexcept;
QBasicAtomicPointer<QMutexData> d_ptr;
static inline QMutexData *dummyLocked() {
return reinterpret_cast<QMutexData *>(quintptr(1));
}
friend class QMutex;
friend class QRecursiveMutex;
friend class QMutexData;
};
class Q_CORE_EXPORT QMutex : public QBasicMutex
{
public:
#if defined(Q_COMPILER_CONSTEXPR)
constexpr QMutex() = default;
#else
QMutex() { d_ptr.storeRelaxed(nullptr); }
#endif
#if QT_DEPRECATED_SINCE(5,15)
enum RecursionMode { NonRecursive, Recursive };
QT_DEPRECATED_VERSION_X(5, 15, "Use QRecursiveMutex instead of a recursive QMutex")
explicit QMutex(RecursionMode mode);
QT_DEPRECATED_VERSION_X(5, 15, "Use QRecursiveMutex instead of a recursive QMutex")
bool isRecursive() const noexcept
{ return QBasicMutex::isRecursive(); }
#endif
~QMutex();
// BasicLockable concept
void lock() QT_MUTEX_LOCK_NOEXCEPT;
bool tryLock(int timeout = 0) QT_MUTEX_LOCK_NOEXCEPT;
// BasicLockable concept
void unlock() noexcept;
// Lockable concept
bool try_lock() QT_MUTEX_LOCK_NOEXCEPT { return tryLock(); }
#if __has_include(<chrono>) || defined(Q_CLANG_QDOC)
// TimedLockable concept
template <class Rep, class Period>
bool try_lock_for(std::chrono::duration<Rep, Period> duration)
{
return tryLock(convertToMilliseconds(duration));
}
// TimedLockable concept
template<class Clock, class Duration>
bool try_lock_until(std::chrono::time_point<Clock, Duration> timePoint)
{
// Implemented in terms of try_lock_for to honor the similar
// requirement in N4606 § 30.4.1.3 [thread.timedmutex.requirements]/12.
return try_lock_for(timePoint - Clock::now());
}
#endif
private:
Q_DISABLE_COPY(QMutex)
template<typename Mutex>
friend class QMutexLocker;
friend class QRecursiveMutex;
friend class ::tst_QMutex;
class QMutexPrivate;
#if __has_include(<chrono>)
namespace QtPrivate
{
template<class Rep, class Period>
static int convertToMilliseconds(std::chrono::duration<Rep, Period> duration)
{
@ -202,25 +91,145 @@ private:
return ms < maxInt ? int(ms) : maxInt;
}
}
#endif
class Q_CORE_EXPORT QBasicMutex
{
Q_DISABLE_COPY_MOVE(QBasicMutex)
public:
constexpr QBasicMutex()
: d_ptr(nullptr)
{}
// BasicLockable concept
inline void lock() QT_MUTEX_LOCK_NOEXCEPT {
if (!fastTryLock())
lockInternal();
}
// BasicLockable concept
inline void unlock() noexcept {
Q_ASSERT(d_ptr.loadRelaxed()); //mutex must be locked
if (!fastTryUnlock())
unlockInternal();
}
bool tryLock() noexcept {
return fastTryLock();
}
// Lockable concept
bool try_lock() noexcept { return tryLock(); }
private:
inline bool fastTryLock() noexcept {
return d_ptr.testAndSetAcquire(nullptr, dummyLocked());
}
inline bool fastTryUnlock() noexcept {
return d_ptr.testAndSetRelease(dummyLocked(), nullptr);
}
void lockInternal() QT_MUTEX_LOCK_NOEXCEPT;
bool lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT;
void unlockInternal() noexcept;
void destroyInternal(QMutexPrivate *d);
QBasicAtomicPointer<QMutexPrivate> d_ptr;
static inline QMutexPrivate *dummyLocked() {
return reinterpret_cast<QMutexPrivate *>(quintptr(1));
}
friend class QMutex;
friend class QMutexPrivate;
};
class Q_CORE_EXPORT QMutex : public QBasicMutex
{
public:
constexpr QMutex() = default;
~QMutex()
{
QMutexPrivate *d = d_ptr.loadRelaxed();
if (d)
destroyInternal(d);
}
#ifdef Q_QDOC
inline void lock() QT_MUTEX_LOCK_NOEXCEPT;
inline void unlock() noexcept;
bool tryLock() noexcept;
#endif
// Lockable concept
bool try_lock() noexcept { return tryLock(); }
using QBasicMutex::tryLock;
bool tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT
{
if (fastTryLock())
return true;
return lockInternal(timeout);
}
#if __has_include(<chrono>) || defined(Q_CLANG_QDOC)
// TimedLockable concept
template <class Rep, class Period>
bool try_lock_for(std::chrono::duration<Rep, Period> duration)
{
return tryLock(QtPrivate::convertToMilliseconds(duration));
}
// TimedLockable concept
template<class Clock, class Duration>
bool try_lock_until(std::chrono::time_point<Clock, Duration> timePoint)
{
// Implemented in terms of try_lock_for to honor the similar
// requirement in N4606 § 30.4.1.3 [thread.timedmutex.requirements]/12.
return try_lock_for(timePoint - Clock::now());
}
#endif
};
class QRecursiveMutex : private QMutex
class QRecursiveMutexPrivate;
class Q_CORE_EXPORT QRecursiveMutex
{
// ### Qt 6: make it independent of QMutex
template<typename Mutex>
friend class QMutexLocker;
Q_DISABLE_COPY_MOVE(QRecursiveMutex)
QRecursiveMutexPrivate *d;
public:
Q_CORE_EXPORT QRecursiveMutex();
Q_CORE_EXPORT ~QRecursiveMutex();
using QMutex::lock;
using QMutex::tryLock;
using QMutex::unlock;
using QMutex::try_lock;
#if __has_include(<chrono>)
using QMutex::try_lock_for;
using QMutex::try_lock_until;
QRecursiveMutex();
~QRecursiveMutex();
// BasicLockable concept
void lock() QT_MUTEX_LOCK_NOEXCEPT;
bool tryLock(int timeout = 0) QT_MUTEX_LOCK_NOEXCEPT;
// BasicLockable concept
void unlock() noexcept;
// Lockable concept
bool try_lock() QT_MUTEX_LOCK_NOEXCEPT { return tryLock(); }
#if __has_include(<chrono>) || defined(Q_CLANG_QDOC)
// TimedLockable concept
template <class Rep, class Period>
bool try_lock_for(std::chrono::duration<Rep, Period> duration)
{
return tryLock(QtPrivate::convertToMilliseconds(duration));
}
// TimedLockable concept
template<class Clock, class Duration>
bool try_lock_until(std::chrono::time_point<Clock, Duration> timePoint)
{
// Implemented in terms of try_lock_for to honor the similar
// requirement in N4606 § 30.4.1.3 [thread.timedmutex.requirements]/12.
return try_lock_for(timePoint - Clock::now());
}
#endif
};
@ -271,18 +280,16 @@ private:
#else // !QT_CONFIG(thread) && !Q_CLANG_QDOC
class Q_CORE_EXPORT QMutex
class QMutex
{
public:
enum RecursionMode { NonRecursive, Recursive };
inline constexpr explicit QMutex(RecursionMode = NonRecursive) noexcept { }
inline constexpr explicit QMutex() noexcept { }
inline void lock() noexcept {}
inline bool tryLock(int timeout = 0) noexcept { Q_UNUSED(timeout); return true; }
inline bool try_lock() noexcept { return true; }
inline void unlock() noexcept {}
inline bool isRecursive() const noexcept { return true; }
#if __has_include(<chrono>)
template <class Rep, class Period>
@ -306,7 +313,7 @@ private:
class QRecursiveMutex : public QMutex {};
template<typename Mutex>
template <typename Mutex>
class QMutexLocker
{
public:

View File

@ -100,13 +100,13 @@ using namespace QtFutex;
* waiting in the past. We then set the mutex to 0x0 and perform a FUTEX_WAKE.
*/
static inline QMutexData *dummyFutexValue()
static inline QMutexPrivate *dummyFutexValue()
{
return reinterpret_cast<QMutexData *>(quintptr(3));
return reinterpret_cast<QMutexPrivate *>(quintptr(3));
}
template <bool IsTimed> static inline
bool lockInternal_helper(QBasicAtomicPointer<QMutexData> &d_ptr, int timeout = -1, QElapsedTimer *elapsedTimer = nullptr) noexcept
bool lockInternal_helper(QBasicAtomicPointer<QMutexPrivate> &d_ptr, int timeout = -1, QElapsedTimer *elapsedTimer = nullptr) noexcept
{
if (!IsTimed)
timeout = -1;
@ -155,13 +155,11 @@ bool lockInternal_helper(QBasicAtomicPointer<QMutexData> &d_ptr, int timeout = -
void QBasicMutex::lockInternal() noexcept
{
Q_ASSERT(!isRecursive());
lockInternal_helper<false>(d_ptr);
}
bool QBasicMutex::lockInternal(int timeout) noexcept
{
Q_ASSERT(!isRecursive());
QElapsedTimer elapsedTimer;
elapsedTimer.start();
return lockInternal_helper<true>(d_ptr, timeout, &elapsedTimer);
@ -169,11 +167,10 @@ bool QBasicMutex::lockInternal(int timeout) noexcept
void QBasicMutex::unlockInternal() noexcept
{
QMutexData *d = d_ptr.loadRelaxed();
QMutexPrivate *d = d_ptr.loadRelaxed();
Q_ASSERT(d); //we must be locked
Q_ASSERT(d != dummyLocked()); // testAndSetRelease(dummyLocked(), 0) failed
Q_UNUSED(d);
Q_ASSERT(!isRecursive());
d_ptr.storeRelease(nullptr);
futexWakeOne(d_ptr);

View File

@ -76,16 +76,8 @@ struct timespec;
QT_BEGIN_NAMESPACE
class QMutexData
{
public:
bool recursive;
QMutexData(QMutex::RecursionMode mode = QMutex::NonRecursive)
: recursive(mode == QMutex::Recursive) {}
};
#if !defined(QT_LINUX_FUTEX)
class QMutexPrivate : public QMutexData
class QMutexPrivate
{
public:
~QMutexPrivate();

View File

@ -49,7 +49,7 @@ QMutexPrivate::QMutexPrivate()
event = CreateEvent(0, FALSE, FALSE, 0);
if (!event)
qWarning("QMutexData::QMutexData: Cannot create event");
qWarning("QMutexPrivate::QMutexPrivate: Cannot create event");
}
QMutexPrivate::~QMutexPrivate()

View File

@ -213,10 +213,6 @@ bool QWaitCondition::wait(QMutex *mutex, QDeadlineTimer deadline)
{
if (! mutex)
return false;
if (static_cast<QBasicMutex *>(mutex)->isRecursive()) {
qWarning("QWaitCondition: cannot wait on recursive mutexes");
return false;
}
report_error(pthread_mutex_lock(&d->mutex), "QWaitCondition::wait()", "mutex lock");
++d->waiters;

View File

@ -134,7 +134,7 @@ QT_CLASS_LIB(QBasicAtomicInt, QtCore, qbasicatomic.h)
QT_CLASS_LIB(QBasicAtomicPointer, QtCore, qbasicatomic.h)
QT_CLASS_LIB(QMutex, QtCore, qmutex.h)
QT_CLASS_LIB(QMutexLocker, QtCore, qmutex.h)
QT_CLASS_LIB(QMutexData, QtCore, qmutex.h)
QT_CLASS_LIB(QMutexPrivate, QtCore, qmutex.h)
QT_CLASS_LIB(QMutex, QtCore, qmutex.h)
QT_CLASS_LIB(QMutexLocker, QtCore, qmutex.h)
QT_CLASS_LIB(QReadWriteLock, QtCore, qreadwritelock.h)

View File

@ -186,7 +186,7 @@ void tst_QMutex::convertToMilliseconds()
#define DO(Rep, Period, val) \
do { \
const std::chrono::duration<Rep, Period> wait((val)); \
QCOMPARE(QMutex::convertToMilliseconds(wait), expected); \
QCOMPARE(QtPrivate::convertToMilliseconds(wait), expected); \
} while (0)
CASE(Nanoseconds, std::nano);