diff options
Diffstat (limited to 'tests/auto/corelib/thread/qmutex/tst_qmutex.cpp')
-rw-r--r-- | tests/auto/corelib/thread/qmutex/tst_qmutex.cpp | 1179 |
1 files changed, 934 insertions, 245 deletions
diff --git a/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp b/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp index 594bae674c..bf778e9fd1 100644 --- a/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp +++ b/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp @@ -31,16 +31,38 @@ #include <qatomic.h> #include <qcoreapplication.h> -#include <qdatetime.h> +#include <qelapsedtimer.h> #include <qmutex.h> #include <qthread.h> #include <qwaitcondition.h> +#ifdef Q_OS_WIN +#include <private/qsystemlibrary_p.h> +#include <cmath> +#endif + class tst_QMutex : public QObject { Q_OBJECT +public: + enum class TimeUnit { + Nanoseconds, + Microseconds, + Milliseconds, + Seconds, + }; + Q_ENUM(TimeUnit); + private slots: - void tryLock(); + void initTestCase(); + void convertToMilliseconds_data(); + void convertToMilliseconds(); + 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 stressTest(); void tryLockRace(); @@ -48,6 +70,8 @@ private slots: void tryLockNegative_data(); void tryLockNegative(); void moreStress(); +private: + void initializeSystemTimersResolution(); }; static const int iterations = 100; @@ -58,257 +82,922 @@ QSemaphore testsTurn; QSemaphore threadsTurn; enum { waitTime = 100 }; +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 + resolution of the underlying timer is too coarse. E.g.: on Windows + WaitForSingleObjectEx does *not* use high resolution multimedia timers, and + it's actually very coarse, about 16msec by default. + + Try to find out the timer resolution in here, so that the tryLock tests can + actually take into account early wakes. +*/ +void tst_QMutex::initializeSystemTimersResolution() +{ +#ifdef Q_OS_WIN + // according to MSDN, Windows can default up to this + systemTimersResolution = 16; + + // private API. There's no way on Windows to otherwise know the + // actual resolution of the application's timers (you can only set it) + // cf. https://stackoverflow.com/questions/7685762/windows-7-timing-functions-how-to-use-getsystemtimeadjustment-correctly/11743614#11743614 + typedef NTSTATUS (NTAPI *NtQueryTimerResolutionType)(OUT PULONG MinimumResolution, + OUT PULONG MaximumResolution, + OUT PULONG ActualResolution); + + const NtQueryTimerResolutionType NtQueryTimerResolutionPtr = + reinterpret_cast<NtQueryTimerResolutionType>(QSystemLibrary::resolve(QStringLiteral("ntdll"), "NtQueryTimerResolution")); + + if (!NtQueryTimerResolutionPtr) + return; + + ULONG minimumResolution; + ULONG maximumResolution; + ULONG actualResolution; + + if (!NtQueryTimerResolutionPtr(&minimumResolution, &maximumResolution, &actualResolution)) { + // the result is in 100ns units => adjust to msec + const double actualResolutionMsec = actualResolution / 10000.0; + systemTimersResolution = static_cast<int>(std::ceil(actualResolutionMsec)); + } +#endif // Q_OS_WIN +} + +void tst_QMutex::initTestCase() +{ + initializeSystemTimersResolution(); +} -void tst_QMutex::tryLock() +void tst_QMutex::convertToMilliseconds_data() { - // test non-recursive mutex + QTest::addColumn<TimeUnit>("unit"); + QTest::addColumn<double>("doubleValue"); + QTest::addColumn<qint64>("intValue"); + QTest::addColumn<qint64>("expected"); + + + auto add = [](TimeUnit unit, double d, long long i, qint64 expected) { + const QScopedArrayPointer<char> enumName(QTest::toString(unit)); + QTest::newRow(qPrintable(QString::asprintf("%s:%f:%lld", enumName.data(), d, i))) + << unit << d << qint64(i) << expected; + }; + + auto forAllUnitsAdd = [=](double d, long long i, qint64 expected) { + for (auto unit : {TimeUnit::Nanoseconds, TimeUnit::Microseconds, TimeUnit::Milliseconds, TimeUnit::Seconds}) + add(unit, d, i, expected); + }; + + forAllUnitsAdd(-0.5, -1, 0); // all negative values result in 0 + + forAllUnitsAdd(0, 0, 0); + + add(TimeUnit::Nanoseconds, 1, 1, 1); + add(TimeUnit::Nanoseconds, 1000 * 1000, 1000 * 1000, 1); + add(TimeUnit::Nanoseconds, 1000 * 1000 + 0.5, 1000 * 1000 + 1, 2); + + add(TimeUnit::Microseconds, 1, 1, 1); + add(TimeUnit::Microseconds, 1000, 1000, 1); + add(TimeUnit::Microseconds, 1000 + 0.5, 1000 + 1, 2); + + add(TimeUnit::Milliseconds, 1, 1, 1); + add(TimeUnit::Milliseconds, 1.5, 2, 2); + + add(TimeUnit::Seconds, 0.9991, 1, 1000); + + // + // overflowing int results in INT_MAX (equivalent to a spurious wakeup after ~24 days); check it: + // + + // spot on: + add(TimeUnit::Nanoseconds, INT_MAX * 1000. * 1000, INT_MAX * Q_INT64_C(1000) * 1000, INT_MAX); + add(TimeUnit::Microseconds, INT_MAX * 1000., INT_MAX * Q_INT64_C(1000), INT_MAX); + add(TimeUnit::Milliseconds, INT_MAX, INT_MAX, INT_MAX); + + // minimally above: + add(TimeUnit::Nanoseconds, INT_MAX * 1000. * 1000 + 1, INT_MAX * Q_INT64_C(1000) * 1000 + 1, INT_MAX); + add(TimeUnit::Microseconds, INT_MAX * 1000. + 1, INT_MAX * Q_INT64_C(1000) + 1, INT_MAX); + add(TimeUnit::Milliseconds, INT_MAX + 1., INT_MAX + Q_INT64_C(1), INT_MAX); + add(TimeUnit::Seconds, INT_MAX / 1000. + 1, INT_MAX / 1000 + 1, INT_MAX); + + // minimally below: + add(TimeUnit::Nanoseconds, INT_MAX * 1000. * 1000 - 1, INT_MAX * Q_INT64_C(1000) * 1000 - 1, INT_MAX); + add(TimeUnit::Microseconds, INT_MAX * 1000. - 1, INT_MAX * Q_INT64_C(1000) - 1, INT_MAX); + add(TimeUnit::Milliseconds, INT_MAX - 0.1, INT_MAX , INT_MAX); + +} + +void tst_QMutex::convertToMilliseconds() +{ +#if !QT_HAS_INCLUDE(<chrono>) + QSKIP("This test requires <chrono>"); +#else + QFETCH(TimeUnit, unit); + QFETCH(double, doubleValue); + QFETCH(qint64, intValue); + QFETCH(qint64, expected); + + Q_CONSTEXPR qint64 maxShort = std::numeric_limits<short>::max(); + Q_CONSTEXPR qint64 maxInt = std::numeric_limits<int>::max(); + Q_CONSTEXPR qint64 maxUInt = std::numeric_limits<uint>::max(); + + switch (unit) { +#define CASE(Unit, Period) \ + case TimeUnit::Unit: \ + DO(double, Period, doubleValue); \ + if (intValue < maxShort) \ + DO(short, Period, short(intValue)); \ + if (intValue < maxInt) \ + DO(int, Period, int(intValue)); \ + DO(qint64, Period, intValue); \ + if (intValue >= 0) { \ + if (intValue < maxUInt) \ + DO(uint, Period, uint(intValue)); \ + DO(quint64, Period, quint64(intValue)); \ + } \ + break +#define DO(Rep, Period, val) \ + do { \ + const std::chrono::duration<Rep, Period> wait((val)); \ + QCOMPARE(QMutex::convertToMilliseconds(wait), expected); \ + } while (0) + + CASE(Nanoseconds, std::nano); + CASE(Microseconds, std::micro); + CASE(Milliseconds, std::milli); + CASE(Seconds, std::ratio<1>); +#undef DO +#undef CASE + } +#endif +} + +void tst_QMutex::tryLock_non_recursive() +{ + class Thread : public QThread { - class Thread : public QThread + public: + void run() { - public: - void run() - { - testsTurn.release(); - - // TEST 1: thread can't acquire lock - threadsTurn.acquire(); - QVERIFY(!normalMutex.tryLock()); - testsTurn.release(); - - // TEST 2: thread can acquire lock - threadsTurn.acquire(); - QVERIFY(normalMutex.tryLock()); - QVERIFY(lockCount.testAndSetRelaxed(0, 1)); - QVERIFY(!normalMutex.tryLock()); - QVERIFY(lockCount.testAndSetRelaxed(1, 0)); - normalMutex.unlock(); - testsTurn.release(); - - // TEST 3: thread can't acquire lock, timeout = waitTime - threadsTurn.acquire(); - QTime timer; - timer.start(); - QVERIFY(!normalMutex.tryLock(waitTime)); - QVERIFY(timer.elapsed() >= waitTime); - testsTurn.release(); - - // TEST 4: thread can acquire lock, timeout = waitTime - threadsTurn.acquire(); - timer.start(); - QVERIFY(normalMutex.tryLock(waitTime)); - QVERIFY(timer.elapsed() <= waitTime); - QVERIFY(lockCount.testAndSetRelaxed(0, 1)); - timer.start(); - // it's non-recursive, so the following lock needs to fail - QVERIFY(!normalMutex.tryLock(waitTime)); - QVERIFY(timer.elapsed() >= waitTime); - QVERIFY(lockCount.testAndSetRelaxed(1, 0)); - normalMutex.unlock(); - testsTurn.release(); - - // TEST 5: thread can't acquire lock, timeout = 0 - threadsTurn.acquire(); - QVERIFY(!normalMutex.tryLock(0)); - testsTurn.release(); - - // TEST 6: thread can acquire lock, timeout = 0 - threadsTurn.acquire(); - timer.start(); - QVERIFY(normalMutex.tryLock(0)); - QVERIFY(timer.elapsed() < waitTime); - QVERIFY(lockCount.testAndSetRelaxed(0, 1)); - QVERIFY(!normalMutex.tryLock(0)); - 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.tryLock(3000)); - QVERIFY(timer.elapsed() < 3000); - normalMutex.unlock(); - testsTurn.release(); - - threadsTurn.acquire(); - } - }; + testsTurn.release(); + + // TEST 1: thread can't acquire lock + threadsTurn.acquire(); + QVERIFY(!normalMutex.tryLock()); + testsTurn.release(); + + // TEST 2: thread can acquire lock + threadsTurn.acquire(); + QVERIFY(normalMutex.tryLock()); + QVERIFY(lockCount.testAndSetRelaxed(0, 1)); + QVERIFY(!normalMutex.tryLock()); + 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.tryLock(waitTime)); + QVERIFY(timer.elapsed() >= waitTime - systemTimersResolution); + testsTurn.release(); + + // TEST 4: thread can acquire lock, timeout = waitTime + threadsTurn.acquire(); + timer.start(); + QVERIFY(normalMutex.tryLock(waitTime)); + 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.tryLock(waitTime)); + 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.tryLock(0)); + testsTurn.release(); + + // TEST 6: thread can acquire lock, timeout = 0 + threadsTurn.acquire(); + timer.start(); + QVERIFY(normalMutex.tryLock(0)); + QVERIFY(timer.elapsed() < waitTime + systemTimersResolution); + QVERIFY(lockCount.testAndSetRelaxed(0, 1)); + QVERIFY(!normalMutex.tryLock(0)); + 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.tryLock(3000)); + QVERIFY(timer.elapsed() < 3000 + systemTimersResolution); + normalMutex.unlock(); + testsTurn.release(); + + threadsTurn.acquire(); + } + }; - Thread thread; - thread.start(); + 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(); +} - // 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(); - } +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 +} - // test recursive mutex +void tst_QMutex::try_lock_until_non_recursive() +{ +#if !QT_HAS_INCLUDE(<chrono>) + QSKIP("This test requires <chrono>"); +#else + class Thread : public QThread { - class Thread : public QThread + public: + void run() { - public: - void run() - { - testsTurn.release(); - - threadsTurn.acquire(); - QVERIFY(!recursiveMutex.tryLock()); - testsTurn.release(); - - threadsTurn.acquire(); - QVERIFY(recursiveMutex.tryLock()); - QVERIFY(lockCount.testAndSetRelaxed(0, 1)); - QVERIFY(recursiveMutex.tryLock()); - QVERIFY(lockCount.testAndSetRelaxed(1, 2)); - QVERIFY(lockCount.testAndSetRelaxed(2, 1)); - recursiveMutex.unlock(); - QVERIFY(lockCount.testAndSetRelaxed(1, 0)); - recursiveMutex.unlock(); - testsTurn.release(); - - threadsTurn.acquire(); - QTime timer; - timer.start(); - QVERIFY(!recursiveMutex.tryLock(waitTime)); - QVERIFY(timer.elapsed() >= waitTime); - QVERIFY(!recursiveMutex.tryLock(0)); - testsTurn.release(); - - threadsTurn.acquire(); - timer.start(); - QVERIFY(recursiveMutex.tryLock(waitTime)); - QVERIFY(timer.elapsed() <= waitTime); - QVERIFY(lockCount.testAndSetRelaxed(0, 1)); - QVERIFY(recursiveMutex.tryLock(waitTime)); - 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.tryLock(0)); - QVERIFY(!recursiveMutex.tryLock(0)); - testsTurn.release(); - - threadsTurn.acquire(); - timer.start(); - QVERIFY(recursiveMutex.tryLock(0)); - QVERIFY(timer.elapsed() < waitTime); - QVERIFY(lockCount.testAndSetRelaxed(0, 1)); - QVERIFY(recursiveMutex.tryLock(0)); - QVERIFY(lockCount.testAndSetRelaxed(1, 2)); - QVERIFY(lockCount.testAndSetRelaxed(2, 1)); - recursiveMutex.unlock(); - QVERIFY(lockCount.testAndSetRelaxed(1, 0)); - recursiveMutex.unlock(); - testsTurn.release(); - - threadsTurn.acquire(); - } - }; + 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(); + 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 +} - // 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(); - } +void tst_QMutex::tryLock_recursive() +{ + class Thread : public QThread + { + public: + void run() + { + testsTurn.release(); + + threadsTurn.acquire(); + QVERIFY(!recursiveMutex.tryLock()); + testsTurn.release(); + + threadsTurn.acquire(); + QVERIFY(recursiveMutex.tryLock()); + QVERIFY(lockCount.testAndSetRelaxed(0, 1)); + QVERIFY(recursiveMutex.tryLock()); + 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.tryLock(waitTime)); + QVERIFY(timer.elapsed() >= waitTime - systemTimersResolution); + QVERIFY(!recursiveMutex.tryLock(0)); + testsTurn.release(); + + threadsTurn.acquire(); + timer.start(); + QVERIFY(recursiveMutex.tryLock(waitTime)); + QVERIFY(timer.elapsed() <= waitTime + systemTimersResolution); + QVERIFY(lockCount.testAndSetRelaxed(0, 1)); + QVERIFY(recursiveMutex.tryLock(waitTime)); + 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.tryLock(0)); + QVERIFY(!recursiveMutex.tryLock(0)); + testsTurn.release(); + + threadsTurn.acquire(); + timer.start(); + QVERIFY(recursiveMutex.tryLock(0)); + QVERIFY(timer.elapsed() < waitTime + systemTimersResolution); + QVERIFY(lockCount.testAndSetRelaxed(0, 1)); + QVERIFY(recursiveMutex.tryLock(0)); + 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(); +} + +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 @@ -439,7 +1128,7 @@ enum { one_minute = 6 * 1000, //not really one minute, but else it is too long. class StressTestThread : public QThread { - QTime t; + QElapsedTimer t; public: static QBasicAtomicInt lockCount; static QBasicAtomicInt sentinel; @@ -491,7 +1180,7 @@ public: void run() { - QTime t; + QElapsedTimer t; t.start(); do { if (mutex.tryLock()) @@ -619,7 +1308,7 @@ void tst_QMutex::tryLockNegative() class MoreStressTestThread : public QThread { - QTime t; + QElapsedTimer t; public: static QAtomicInt lockCount; static QAtomicInt sentinel[threadCount]; |