Q(Basic)Mutex: add try_lock{,_for,_until} for STL compatibility
Now QBasicMutex is Lockable and QMutex is TimedLockable, which means they can be used in std::lock_guard, std::unique_lock, std::lock, etc. [ChangeLog][QtCore][QMutex] QMutex now fully models the TimedLockable concept by providing the try_lock, try_lock_for and try_lock_until functions, therefore making it usable in Standard Library lock management classes and functions. Change-Id: I7c691481a5781a696701e1ab78186b5cefbd6a87 Reviewed-by: Olivier Goffart (Woboq GmbH) <ogoffart@woboq.com>
This commit is contained in:
parent
a8cf3b0257
commit
a594f85d54
@ -264,6 +264,61 @@ bool QMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT
|
|||||||
return lockInternal(timeout);
|
return lockInternal(timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! \fn bool QMutex::try_lock()
|
||||||
|
\since 5.8
|
||||||
|
|
||||||
|
This function is provided for compatibility with the Standard Library
|
||||||
|
concept \c Lockable. It is equivalent to tryLock().
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \fn bool QMutex::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 most \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 is different 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 if this mutex is a
|
||||||
|
\l{QMutex::Recursive}{recursive mutex}. If this mutex is a
|
||||||
|
\l{QMutex::NonRecursive}{non-recursive mutex}, this function will
|
||||||
|
\e always return false when attempting to lock the mutex
|
||||||
|
recursively.
|
||||||
|
|
||||||
|
\sa lock(), unlock()
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \fn bool QMutex::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 most 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 is different 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 if this mutex is a
|
||||||
|
\l{QMutex::Recursive}{recursive mutex}. If this mutex is a
|
||||||
|
\l{QMutex::NonRecursive}{non-recursive mutex}, this function will
|
||||||
|
\e always return false when attempting to lock the mutex
|
||||||
|
recursively.
|
||||||
|
|
||||||
|
\sa lock(), unlock()
|
||||||
|
*/
|
||||||
|
|
||||||
/*! \fn void QMutex::unlock()
|
/*! \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
|
||||||
|
@ -44,6 +44,10 @@
|
|||||||
#include <QtCore/qatomic.h>
|
#include <QtCore/qatomic.h>
|
||||||
#include <new>
|
#include <new>
|
||||||
|
|
||||||
|
#if QT_HAS_INCLUDE(<chrono>)
|
||||||
|
# include <chrono>
|
||||||
|
#endif
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
|
||||||
@ -60,11 +64,13 @@ class QMutexData;
|
|||||||
class Q_CORE_EXPORT QBasicMutex
|
class Q_CORE_EXPORT QBasicMutex
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
// BasicLockable concept
|
||||||
inline void lock() QT_MUTEX_LOCK_NOEXCEPT {
|
inline void lock() QT_MUTEX_LOCK_NOEXCEPT {
|
||||||
if (!fastTryLock())
|
if (!fastTryLock())
|
||||||
lockInternal();
|
lockInternal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BasicLockable concept
|
||||||
inline void unlock() Q_DECL_NOTHROW {
|
inline void unlock() Q_DECL_NOTHROW {
|
||||||
Q_ASSERT(d_ptr.load()); //mutex must be locked
|
Q_ASSERT(d_ptr.load()); //mutex must be locked
|
||||||
if (!fastTryUnlock())
|
if (!fastTryUnlock())
|
||||||
@ -75,6 +81,9 @@ public:
|
|||||||
return fastTryLock();
|
return fastTryLock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lockable concept
|
||||||
|
bool try_lock() Q_DECL_NOTHROW { return tryLock(); }
|
||||||
|
|
||||||
bool isRecursive() Q_DECL_NOTHROW; //### Qt6: remove me
|
bool isRecursive() Q_DECL_NOTHROW; //### Qt6: remove me
|
||||||
bool isRecursive() const Q_DECL_NOTHROW;
|
bool isRecursive() const Q_DECL_NOTHROW;
|
||||||
|
|
||||||
@ -112,10 +121,41 @@ public:
|
|||||||
explicit QMutex(RecursionMode mode = NonRecursive);
|
explicit QMutex(RecursionMode mode = NonRecursive);
|
||||||
~QMutex();
|
~QMutex();
|
||||||
|
|
||||||
|
// BasicLockable concept
|
||||||
void lock() QT_MUTEX_LOCK_NOEXCEPT;
|
void lock() QT_MUTEX_LOCK_NOEXCEPT;
|
||||||
bool tryLock(int timeout = 0) QT_MUTEX_LOCK_NOEXCEPT;
|
bool tryLock(int timeout = 0) QT_MUTEX_LOCK_NOEXCEPT;
|
||||||
|
// BasicLockable concept
|
||||||
void unlock() Q_DECL_NOTHROW;
|
void unlock() Q_DECL_NOTHROW;
|
||||||
|
|
||||||
|
// Lockable concept
|
||||||
|
bool try_lock() QT_MUTEX_LOCK_NOEXCEPT { return tryLock(); }
|
||||||
|
|
||||||
|
#if QT_HAS_INCLUDE(<chrono>)
|
||||||
|
// TimedLockable concept
|
||||||
|
template <class Rep, class Period>
|
||||||
|
bool try_lock_for(std::chrono::duration<Rep, Period> duration)
|
||||||
|
{
|
||||||
|
// § 30.4.1.3.5 [thread.timedmutex.requirements] specifies that a
|
||||||
|
// duration less than or equal to duration.zero() shall result in a
|
||||||
|
// try_lock, unlike QMutex's tryLock with a negative duration which
|
||||||
|
// results in a lock.
|
||||||
|
|
||||||
|
if (duration <= duration.zero())
|
||||||
|
return tryLock(0);
|
||||||
|
return tryLock(std::chrono::duration_cast<std::chrono::milliseconds>(duration).count());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 § 30.4.1.3.12 [thread.timedmutex.requirements]
|
||||||
|
|
||||||
|
return try_lock_for(timePoint - Clock::now());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
bool isRecursive() const Q_DECL_NOTHROW
|
bool isRecursive() const Q_DECL_NOTHROW
|
||||||
{ return QBasicMutex::isRecursive(); }
|
{ return QBasicMutex::isRecursive(); }
|
||||||
|
|
||||||
@ -189,9 +229,26 @@ public:
|
|||||||
|
|
||||||
inline void lock() Q_DECL_NOTHROW {}
|
inline void lock() Q_DECL_NOTHROW {}
|
||||||
inline bool tryLock(int timeout = 0) Q_DECL_NOTHROW { Q_UNUSED(timeout); return true; }
|
inline bool tryLock(int timeout = 0) Q_DECL_NOTHROW { Q_UNUSED(timeout); return true; }
|
||||||
|
inline bool try_lock() Q_DECL_NOTHROW { return true; }
|
||||||
inline void unlock() Q_DECL_NOTHROW {}
|
inline void unlock() Q_DECL_NOTHROW {}
|
||||||
inline bool isRecursive() const Q_DECL_NOTHROW { return true; }
|
inline bool isRecursive() const Q_DECL_NOTHROW { return true; }
|
||||||
|
|
||||||
|
#if QT_HAS_INCLUDE(<chrono>) || defined(Q_QDOC)
|
||||||
|
template <class Rep, class Period>
|
||||||
|
inline bool try_lock_for(std::chrono::duration<Rep, Period> duration) Q_DECL_NOTHROW
|
||||||
|
{
|
||||||
|
Q_UNUSED(duration);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Clock, class Duration>
|
||||||
|
inline bool try_lock_until(std::chrono::time_point<Clock, Duration> timePoint) Q_DECL_NOTHROW
|
||||||
|
{
|
||||||
|
Q_UNUSED(timePoint);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Q_DISABLE_COPY(QMutex)
|
Q_DISABLE_COPY(QMutex)
|
||||||
};
|
};
|
||||||
|
@ -46,7 +46,12 @@ class tst_QMutex : public QObject
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
private slots:
|
private slots:
|
||||||
void initTestCase();
|
void initTestCase();
|
||||||
void tryLock();
|
void tryLock_non_recursive();
|
||||||
|
void try_lock_for_non_recursive();
|
||||||
|
void try_lock_until_non_recursive();
|
||||||
|
void tryLock_recursive();
|
||||||
|
void try_lock_for_recursive();
|
||||||
|
void try_lock_until_recursive();
|
||||||
void lock_unlock_locked_tryLock();
|
void lock_unlock_locked_tryLock();
|
||||||
void stressTest();
|
void stressTest();
|
||||||
void tryLockRace();
|
void tryLockRace();
|
||||||
@ -68,6 +73,10 @@ QSemaphore threadsTurn;
|
|||||||
enum { waitTime = 100 };
|
enum { waitTime = 100 };
|
||||||
uint systemTimersResolution = 1;
|
uint systemTimersResolution = 1;
|
||||||
|
|
||||||
|
#if QT_HAS_INCLUDE(<chrono>)
|
||||||
|
static Q_CONSTEXPR std::chrono::milliseconds waitTimeAsDuration(waitTime);
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Depending on the OS, tryWaits may return early than expected because of the
|
Depending on the OS, tryWaits may return early than expected because of the
|
||||||
resolution of the underlying timer is too coarse. E.g.: on Windows
|
resolution of the underlying timer is too coarse. E.g.: on Windows
|
||||||
@ -113,10 +122,8 @@ void tst_QMutex::initTestCase()
|
|||||||
initializeSystemTimersResolution();
|
initializeSystemTimersResolution();
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_QMutex::tryLock()
|
void tst_QMutex::tryLock_non_recursive()
|
||||||
{
|
{
|
||||||
// test non-recursive mutex
|
|
||||||
{
|
|
||||||
class Thread : public QThread
|
class Thread : public QThread
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -238,10 +245,267 @@ void tst_QMutex::tryLock()
|
|||||||
testsTurn.acquire();
|
testsTurn.acquire();
|
||||||
threadsTurn.release();
|
threadsTurn.release();
|
||||||
thread.wait();
|
thread.wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
// test recursive mutex
|
void tst_QMutex::try_lock_for_non_recursive() {
|
||||||
|
#if !QT_HAS_INCLUDE(<chrono>)
|
||||||
|
QSKIP("This test requires <chrono>");
|
||||||
|
#else
|
||||||
|
class Thread : public QThread
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
void run()
|
||||||
|
{
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
// TEST 1: thread can't acquire lock
|
||||||
|
threadsTurn.acquire();
|
||||||
|
QVERIFY(!normalMutex.try_lock());
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
// TEST 2: thread can acquire lock
|
||||||
|
threadsTurn.acquire();
|
||||||
|
QVERIFY(normalMutex.try_lock());
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
|
QVERIFY(!normalMutex.try_lock());
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
||||||
|
normalMutex.unlock();
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
// TEST 3: thread can't acquire lock, timeout = waitTime
|
||||||
|
threadsTurn.acquire();
|
||||||
|
QElapsedTimer timer;
|
||||||
|
timer.start();
|
||||||
|
QVERIFY(!normalMutex.try_lock_for(waitTimeAsDuration));
|
||||||
|
QVERIFY(timer.elapsed() >= waitTime - systemTimersResolution);
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
// TEST 4: thread can acquire lock, timeout = waitTime
|
||||||
|
threadsTurn.acquire();
|
||||||
|
timer.start();
|
||||||
|
QVERIFY(normalMutex.try_lock_for(waitTimeAsDuration));
|
||||||
|
QVERIFY(timer.elapsed() <= waitTime + systemTimersResolution);
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
|
timer.start();
|
||||||
|
// it's non-recursive, so the following lock needs to fail
|
||||||
|
QVERIFY(!normalMutex.try_lock_for(waitTimeAsDuration));
|
||||||
|
QVERIFY(timer.elapsed() >= waitTime - systemTimersResolution);
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
||||||
|
normalMutex.unlock();
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
// TEST 5: thread can't acquire lock, timeout = 0
|
||||||
|
threadsTurn.acquire();
|
||||||
|
QVERIFY(!normalMutex.try_lock_for(std::chrono::milliseconds::zero()));
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
// TEST 6: thread can acquire lock, timeout = 0
|
||||||
|
threadsTurn.acquire();
|
||||||
|
timer.start();
|
||||||
|
QVERIFY(normalMutex.try_lock_for(std::chrono::milliseconds::zero()));
|
||||||
|
QVERIFY(timer.elapsed() < waitTime + systemTimersResolution);
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
|
QVERIFY(!normalMutex.try_lock_for(std::chrono::milliseconds::zero()));
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
||||||
|
normalMutex.unlock();
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
// TEST 7 overflow: thread can acquire lock, timeout = 3000 (QTBUG-24795)
|
||||||
|
threadsTurn.acquire();
|
||||||
|
timer.start();
|
||||||
|
QVERIFY(normalMutex.try_lock_for(std::chrono::milliseconds(3000)));
|
||||||
|
QVERIFY(timer.elapsed() < 3000 + systemTimersResolution);
|
||||||
|
normalMutex.unlock();
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
threadsTurn.acquire();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Thread thread;
|
||||||
|
thread.start();
|
||||||
|
|
||||||
|
// TEST 1: thread can't acquire lock
|
||||||
|
testsTurn.acquire();
|
||||||
|
normalMutex.lock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
|
threadsTurn.release();
|
||||||
|
|
||||||
|
// TEST 2: thread can acquire lock
|
||||||
|
testsTurn.acquire();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
||||||
|
normalMutex.unlock();
|
||||||
|
threadsTurn.release();
|
||||||
|
|
||||||
|
// TEST 3: thread can't acquire lock, timeout = waitTime
|
||||||
|
testsTurn.acquire();
|
||||||
|
normalMutex.lock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
|
threadsTurn.release();
|
||||||
|
|
||||||
|
// TEST 4: thread can acquire lock, timeout = waitTime
|
||||||
|
testsTurn.acquire();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
||||||
|
normalMutex.unlock();
|
||||||
|
threadsTurn.release();
|
||||||
|
|
||||||
|
// TEST 5: thread can't acquire lock, timeout = 0
|
||||||
|
testsTurn.acquire();
|
||||||
|
normalMutex.lock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
|
threadsTurn.release();
|
||||||
|
|
||||||
|
// TEST 6: thread can acquire lock, timeout = 0
|
||||||
|
testsTurn.acquire();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
||||||
|
normalMutex.unlock();
|
||||||
|
threadsTurn.release();
|
||||||
|
|
||||||
|
// TEST 7: thread can acquire lock, timeout = 3000 (QTBUG-24795)
|
||||||
|
testsTurn.acquire();
|
||||||
|
normalMutex.lock();
|
||||||
|
threadsTurn.release();
|
||||||
|
QThread::msleep(100);
|
||||||
|
normalMutex.unlock();
|
||||||
|
|
||||||
|
// wait for thread to finish
|
||||||
|
testsTurn.acquire();
|
||||||
|
threadsTurn.release();
|
||||||
|
thread.wait();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QMutex::try_lock_until_non_recursive()
|
||||||
|
{
|
||||||
|
#if !QT_HAS_INCLUDE(<chrono>)
|
||||||
|
QSKIP("This test requires <chrono>");
|
||||||
|
#else
|
||||||
|
class Thread : public QThread
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void run()
|
||||||
|
{
|
||||||
|
const std::chrono::milliseconds systemTimersResolutionAsDuration(systemTimersResolution);
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
// TEST 1: thread can't acquire lock
|
||||||
|
threadsTurn.acquire();
|
||||||
|
QVERIFY(!normalMutex.try_lock());
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
// TEST 2: thread can acquire lock
|
||||||
|
threadsTurn.acquire();
|
||||||
|
QVERIFY(normalMutex.try_lock());
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
|
QVERIFY(!normalMutex.try_lock());
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
||||||
|
normalMutex.unlock();
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
// TEST 3: thread can't acquire lock, timeout = waitTime
|
||||||
|
threadsTurn.acquire();
|
||||||
|
auto endTimePoint = std::chrono::steady_clock::now() + waitTimeAsDuration;
|
||||||
|
QVERIFY(!normalMutex.try_lock_until(endTimePoint));
|
||||||
|
QVERIFY(std::chrono::steady_clock::now() >= endTimePoint - systemTimersResolutionAsDuration);
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
// TEST 4: thread can acquire lock, timeout = waitTime
|
||||||
|
threadsTurn.acquire();
|
||||||
|
endTimePoint = std::chrono::steady_clock::now() + waitTimeAsDuration;
|
||||||
|
QVERIFY(normalMutex.try_lock_until(endTimePoint));
|
||||||
|
QVERIFY(std::chrono::steady_clock::now() <= endTimePoint + systemTimersResolutionAsDuration);
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
|
endTimePoint = std::chrono::steady_clock::now() + waitTimeAsDuration;
|
||||||
|
// it's non-recursive, so the following lock needs to fail
|
||||||
|
QVERIFY(!normalMutex.try_lock_until(endTimePoint));
|
||||||
|
QVERIFY(std::chrono::steady_clock::now() >= endTimePoint - systemTimersResolutionAsDuration);
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
||||||
|
normalMutex.unlock();
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
// TEST 5: thread can't acquire lock, timeout = 0
|
||||||
|
threadsTurn.acquire();
|
||||||
|
QVERIFY(!normalMutex.try_lock_until(std::chrono::steady_clock::now()));
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
// TEST 6: thread can acquire lock, timeout = 0
|
||||||
|
threadsTurn.acquire();
|
||||||
|
endTimePoint = std::chrono::steady_clock::now() + waitTimeAsDuration;
|
||||||
|
QVERIFY(normalMutex.try_lock_until(std::chrono::steady_clock::now()));
|
||||||
|
QVERIFY(std::chrono::steady_clock::now() < endTimePoint + systemTimersResolutionAsDuration);
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
|
QVERIFY(!normalMutex.try_lock_until(std::chrono::steady_clock::now()));
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
||||||
|
normalMutex.unlock();
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
// TEST 7 overflow: thread can acquire lock, timeout = 3000 (QTBUG-24795)
|
||||||
|
threadsTurn.acquire();
|
||||||
|
endTimePoint = std::chrono::steady_clock::now() + std::chrono::milliseconds(3000);
|
||||||
|
QVERIFY(normalMutex.try_lock_until(endTimePoint));
|
||||||
|
QVERIFY(std::chrono::steady_clock::now() < endTimePoint + systemTimersResolutionAsDuration);
|
||||||
|
normalMutex.unlock();
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
threadsTurn.acquire();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Thread thread;
|
||||||
|
thread.start();
|
||||||
|
|
||||||
|
// TEST 1: thread can't acquire lock
|
||||||
|
testsTurn.acquire();
|
||||||
|
normalMutex.lock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
|
threadsTurn.release();
|
||||||
|
|
||||||
|
// TEST 2: thread can acquire lock
|
||||||
|
testsTurn.acquire();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
||||||
|
normalMutex.unlock();
|
||||||
|
threadsTurn.release();
|
||||||
|
|
||||||
|
// TEST 3: thread can't acquire lock, timeout = waitTime
|
||||||
|
testsTurn.acquire();
|
||||||
|
normalMutex.lock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
|
threadsTurn.release();
|
||||||
|
|
||||||
|
// TEST 4: thread can acquire lock, timeout = waitTime
|
||||||
|
testsTurn.acquire();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
||||||
|
normalMutex.unlock();
|
||||||
|
threadsTurn.release();
|
||||||
|
|
||||||
|
// TEST 5: thread can't acquire lock, timeout = 0
|
||||||
|
testsTurn.acquire();
|
||||||
|
normalMutex.lock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
|
threadsTurn.release();
|
||||||
|
|
||||||
|
// TEST 6: thread can acquire lock, timeout = 0
|
||||||
|
testsTurn.acquire();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
||||||
|
normalMutex.unlock();
|
||||||
|
threadsTurn.release();
|
||||||
|
|
||||||
|
// TEST 7: thread can acquire lock, timeout = 3000 (QTBUG-24795)
|
||||||
|
testsTurn.acquire();
|
||||||
|
normalMutex.lock();
|
||||||
|
threadsTurn.release();
|
||||||
|
QThread::msleep(100);
|
||||||
|
normalMutex.unlock();
|
||||||
|
|
||||||
|
// wait for thread to finish
|
||||||
|
testsTurn.acquire();
|
||||||
|
threadsTurn.release();
|
||||||
|
thread.wait();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QMutex::tryLock_recursive()
|
||||||
|
{
|
||||||
class Thread : public QThread
|
class Thread : public QThread
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -362,7 +626,264 @@ void tst_QMutex::tryLock()
|
|||||||
testsTurn.acquire();
|
testsTurn.acquire();
|
||||||
threadsTurn.release();
|
threadsTurn.release();
|
||||||
thread.wait();
|
thread.wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QMutex::try_lock_for_recursive()
|
||||||
|
{
|
||||||
|
#if !QT_HAS_INCLUDE(<chrono>)
|
||||||
|
QSKIP("This test requires <chrono>");
|
||||||
|
#else
|
||||||
|
class Thread : public QThread
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void run()
|
||||||
|
{
|
||||||
|
const std::chrono::milliseconds systemTimersResolutionAsDuration(systemTimersResolution);
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
threadsTurn.acquire();
|
||||||
|
QVERIFY(!recursiveMutex.try_lock());
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
threadsTurn.acquire();
|
||||||
|
QVERIFY(recursiveMutex.try_lock());
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
|
QVERIFY(recursiveMutex.try_lock());
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 2));
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(2, 1));
|
||||||
|
recursiveMutex.unlock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
||||||
|
recursiveMutex.unlock();
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
threadsTurn.acquire();
|
||||||
|
QElapsedTimer timer;
|
||||||
|
timer.start();
|
||||||
|
QVERIFY(!recursiveMutex.try_lock_for(waitTimeAsDuration));
|
||||||
|
QVERIFY(timer.elapsed() >= waitTime - systemTimersResolution);
|
||||||
|
QVERIFY(!recursiveMutex.try_lock_for(std::chrono::milliseconds::zero()));
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
threadsTurn.acquire();
|
||||||
|
timer.start();
|
||||||
|
QVERIFY(recursiveMutex.try_lock_for(waitTimeAsDuration));
|
||||||
|
QVERIFY(timer.elapsed() <= waitTime + systemTimersResolution);
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
|
QVERIFY(recursiveMutex.try_lock_for(waitTimeAsDuration));
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 2));
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(2, 1));
|
||||||
|
recursiveMutex.unlock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
||||||
|
recursiveMutex.unlock();
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
threadsTurn.acquire();
|
||||||
|
QVERIFY(!recursiveMutex.try_lock_for(std::chrono::milliseconds::zero()));
|
||||||
|
QVERIFY(!recursiveMutex.try_lock_for(std::chrono::milliseconds::zero()));
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
threadsTurn.acquire();
|
||||||
|
timer.start();
|
||||||
|
QVERIFY(recursiveMutex.try_lock_for(std::chrono::milliseconds::zero()));
|
||||||
|
QVERIFY(timer.elapsed() < waitTime + systemTimersResolution);
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
|
QVERIFY(recursiveMutex.try_lock_for(std::chrono::milliseconds::zero()));
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 2));
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(2, 1));
|
||||||
|
recursiveMutex.unlock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
||||||
|
recursiveMutex.unlock();
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
threadsTurn.acquire();
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Thread thread;
|
||||||
|
thread.start();
|
||||||
|
|
||||||
|
// thread can't acquire lock
|
||||||
|
testsTurn.acquire();
|
||||||
|
recursiveMutex.lock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
|
recursiveMutex.lock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 2));
|
||||||
|
threadsTurn.release();
|
||||||
|
|
||||||
|
// thread can acquire lock
|
||||||
|
testsTurn.acquire();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(2, 1));
|
||||||
|
recursiveMutex.unlock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
||||||
|
recursiveMutex.unlock();
|
||||||
|
threadsTurn.release();
|
||||||
|
|
||||||
|
// thread can't acquire lock, timeout = waitTime
|
||||||
|
testsTurn.acquire();
|
||||||
|
recursiveMutex.lock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
|
recursiveMutex.lock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 2));
|
||||||
|
threadsTurn.release();
|
||||||
|
|
||||||
|
// thread can acquire lock, timeout = waitTime
|
||||||
|
testsTurn.acquire();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(2, 1));
|
||||||
|
recursiveMutex.unlock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
||||||
|
recursiveMutex.unlock();
|
||||||
|
threadsTurn.release();
|
||||||
|
|
||||||
|
// thread can't acquire lock, timeout = 0
|
||||||
|
testsTurn.acquire();
|
||||||
|
recursiveMutex.lock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
|
recursiveMutex.lock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 2));
|
||||||
|
threadsTurn.release();
|
||||||
|
|
||||||
|
// thread can acquire lock, timeout = 0
|
||||||
|
testsTurn.acquire();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(2, 1));
|
||||||
|
recursiveMutex.unlock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
||||||
|
recursiveMutex.unlock();
|
||||||
|
threadsTurn.release();
|
||||||
|
|
||||||
|
// stop thread
|
||||||
|
testsTurn.acquire();
|
||||||
|
threadsTurn.release();
|
||||||
|
thread.wait();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QMutex::try_lock_until_recursive()
|
||||||
|
{
|
||||||
|
#if !QT_HAS_INCLUDE(<chrono>)
|
||||||
|
QSKIP("This test requires <chrono>");
|
||||||
|
#else
|
||||||
|
class Thread : public QThread
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void run()
|
||||||
|
{
|
||||||
|
const std::chrono::milliseconds systemTimersResolutionAsDuration(systemTimersResolution);
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
threadsTurn.acquire();
|
||||||
|
QVERIFY(!recursiveMutex.try_lock());
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
threadsTurn.acquire();
|
||||||
|
QVERIFY(recursiveMutex.try_lock());
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
|
QVERIFY(recursiveMutex.try_lock());
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 2));
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(2, 1));
|
||||||
|
recursiveMutex.unlock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
||||||
|
recursiveMutex.unlock();
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
threadsTurn.acquire();
|
||||||
|
auto endTimePoint = std::chrono::steady_clock::now() + waitTimeAsDuration;
|
||||||
|
QVERIFY(!recursiveMutex.try_lock_until(endTimePoint));
|
||||||
|
QVERIFY(std::chrono::steady_clock::now() >= endTimePoint - systemTimersResolutionAsDuration);
|
||||||
|
QVERIFY(!recursiveMutex.try_lock());
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
threadsTurn.acquire();
|
||||||
|
endTimePoint = std::chrono::steady_clock::now() + waitTimeAsDuration;
|
||||||
|
QVERIFY(recursiveMutex.try_lock_until(endTimePoint));
|
||||||
|
QVERIFY(std::chrono::steady_clock::now() <= endTimePoint + systemTimersResolutionAsDuration);
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
|
endTimePoint = std::chrono::steady_clock::now() + waitTimeAsDuration;
|
||||||
|
QVERIFY(recursiveMutex.try_lock_until(endTimePoint));
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 2));
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(2, 1));
|
||||||
|
recursiveMutex.unlock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
||||||
|
recursiveMutex.unlock();
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
threadsTurn.acquire();
|
||||||
|
QVERIFY(!recursiveMutex.try_lock_until(std::chrono::steady_clock::now()));
|
||||||
|
QVERIFY(!recursiveMutex.try_lock_until(std::chrono::steady_clock::now()));
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
threadsTurn.acquire();
|
||||||
|
endTimePoint = std::chrono::steady_clock::now() + waitTimeAsDuration;
|
||||||
|
QVERIFY(recursiveMutex.try_lock_until(std::chrono::steady_clock::now()));
|
||||||
|
QVERIFY(std::chrono::steady_clock::now() <= endTimePoint + systemTimersResolutionAsDuration);
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
|
QVERIFY(recursiveMutex.try_lock_until(std::chrono::steady_clock::now()));
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 2));
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(2, 1));
|
||||||
|
recursiveMutex.unlock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
||||||
|
recursiveMutex.unlock();
|
||||||
|
testsTurn.release();
|
||||||
|
|
||||||
|
threadsTurn.acquire();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Thread thread;
|
||||||
|
thread.start();
|
||||||
|
|
||||||
|
// thread can't acquire lock
|
||||||
|
testsTurn.acquire();
|
||||||
|
recursiveMutex.lock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
|
recursiveMutex.lock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 2));
|
||||||
|
threadsTurn.release();
|
||||||
|
|
||||||
|
// thread can acquire lock
|
||||||
|
testsTurn.acquire();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(2, 1));
|
||||||
|
recursiveMutex.unlock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
||||||
|
recursiveMutex.unlock();
|
||||||
|
threadsTurn.release();
|
||||||
|
|
||||||
|
// thread can't acquire lock, timeout = waitTime
|
||||||
|
testsTurn.acquire();
|
||||||
|
recursiveMutex.lock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
|
recursiveMutex.lock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 2));
|
||||||
|
threadsTurn.release();
|
||||||
|
|
||||||
|
// thread can acquire lock, timeout = waitTime
|
||||||
|
testsTurn.acquire();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(2, 1));
|
||||||
|
recursiveMutex.unlock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
||||||
|
recursiveMutex.unlock();
|
||||||
|
threadsTurn.release();
|
||||||
|
|
||||||
|
// thread can't acquire lock, timeout = 0
|
||||||
|
testsTurn.acquire();
|
||||||
|
recursiveMutex.lock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
||||||
|
recursiveMutex.lock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 2));
|
||||||
|
threadsTurn.release();
|
||||||
|
|
||||||
|
// thread can acquire lock, timeout = 0
|
||||||
|
testsTurn.acquire();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(2, 1));
|
||||||
|
recursiveMutex.unlock();
|
||||||
|
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
||||||
|
recursiveMutex.unlock();
|
||||||
|
threadsTurn.release();
|
||||||
|
|
||||||
|
// stop thread
|
||||||
|
testsTurn.acquire();
|
||||||
|
threadsTurn.release();
|
||||||
|
thread.wait();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
class mutex_Thread : public QThread
|
class mutex_Thread : public QThread
|
||||||
|
Loading…
x
Reference in New Issue
Block a user