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:
parent
77d812683f
commit
d4b206b246
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 *¤t) noexcept {
|
||||
return d_ptr.testAndSetAcquire(nullptr, dummyLocked(), current);
|
||||
}
|
||||
inline bool fastTryUnlock(QMutexData *¤t) 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:
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user