diff options
author | Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> | 2016-07-16 18:06:48 +0200 |
---|---|---|
committer | Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> | 2016-07-22 23:18:27 +0000 |
commit | 7814bf126a50b7afa1dc46059b685f82f1b171f0 (patch) | |
tree | 440f310f3fb1e813e0d24a2c0070c71b523222a2 /tests/auto/corelib/thread/qmutex | |
parent | 8ea51f8aaa426fa14393b83082f9eec076d7d72a (diff) |
Use QElapsedTimer in tst_qmutex
This requires fixing the test on Windows: QMutex internally uses
WaitForSingleObjectEx which can wake up early, according to the system
timer resolution:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms687069(v=vs.85).aspx#waitfunctionsandtime-outintervals
QTime must be so slow that it hides the early wakes, but QElapsedTimer is
accurate enough to make the test fail unless we add back some tolerance to
compensate for the early wakeups.
Change-Id: I20b38af9c87a0b0e38a19b9bff1c3c24975c78f5
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Diffstat (limited to 'tests/auto/corelib/thread/qmutex')
-rw-r--r-- | tests/auto/corelib/thread/qmutex/qmutex.pro | 1 | ||||
-rw-r--r-- | tests/auto/corelib/thread/qmutex/tst_qmutex.cpp | 82 |
2 files changed, 69 insertions, 14 deletions
diff --git a/tests/auto/corelib/thread/qmutex/qmutex.pro b/tests/auto/corelib/thread/qmutex/qmutex.pro index 229e57eb89..cb9d364b71 100644 --- a/tests/auto/corelib/thread/qmutex/qmutex.pro +++ b/tests/auto/corelib/thread/qmutex/qmutex.pro @@ -2,3 +2,4 @@ CONFIG += testcase TARGET = tst_qmutex QT = core testlib SOURCES = tst_qmutex.cpp +win32:QT += core-private diff --git a/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp b/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp index 594bae674c..5037e42beb 100644 --- a/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp +++ b/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp @@ -31,15 +31,21 @@ #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 private slots: + void initTestCase(); void tryLock(); void lock_unlock_locked_tryLock(); void stressTest(); @@ -48,6 +54,8 @@ private slots: void tryLockNegative_data(); void tryLockNegative(); void moreStress(); +private: + void initializeSystemTimersResolution(); }; static const int iterations = 100; @@ -58,6 +66,52 @@ QSemaphore testsTurn; QSemaphore threadsTurn; enum { waitTime = 100 }; +uint systemTimersResolution = 1; + +/* + 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() { @@ -86,22 +140,22 @@ void tst_QMutex::tryLock() // TEST 3: thread can't acquire lock, timeout = waitTime threadsTurn.acquire(); - QTime timer; + QElapsedTimer timer; timer.start(); QVERIFY(!normalMutex.tryLock(waitTime)); - QVERIFY(timer.elapsed() >= 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); + 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); + QVERIFY(timer.elapsed() >= waitTime - systemTimersResolution); QVERIFY(lockCount.testAndSetRelaxed(1, 0)); normalMutex.unlock(); testsTurn.release(); @@ -115,7 +169,7 @@ void tst_QMutex::tryLock() threadsTurn.acquire(); timer.start(); QVERIFY(normalMutex.tryLock(0)); - QVERIFY(timer.elapsed() < waitTime); + QVERIFY(timer.elapsed() < waitTime + systemTimersResolution); QVERIFY(lockCount.testAndSetRelaxed(0, 1)); QVERIFY(!normalMutex.tryLock(0)); QVERIFY(lockCount.testAndSetRelaxed(1, 0)); @@ -126,7 +180,7 @@ void tst_QMutex::tryLock() threadsTurn.acquire(); timer.start(); QVERIFY(normalMutex.tryLock(3000)); - QVERIFY(timer.elapsed() < 3000); + QVERIFY(timer.elapsed() < 3000 + systemTimersResolution); normalMutex.unlock(); testsTurn.release(); @@ -211,17 +265,17 @@ void tst_QMutex::tryLock() testsTurn.release(); threadsTurn.acquire(); - QTime timer; + QElapsedTimer timer; timer.start(); QVERIFY(!recursiveMutex.tryLock(waitTime)); - QVERIFY(timer.elapsed() >= waitTime); + QVERIFY(timer.elapsed() >= waitTime - systemTimersResolution); QVERIFY(!recursiveMutex.tryLock(0)); testsTurn.release(); threadsTurn.acquire(); timer.start(); QVERIFY(recursiveMutex.tryLock(waitTime)); - QVERIFY(timer.elapsed() <= waitTime); + QVERIFY(timer.elapsed() <= waitTime + systemTimersResolution); QVERIFY(lockCount.testAndSetRelaxed(0, 1)); QVERIFY(recursiveMutex.tryLock(waitTime)); QVERIFY(lockCount.testAndSetRelaxed(1, 2)); @@ -239,7 +293,7 @@ void tst_QMutex::tryLock() threadsTurn.acquire(); timer.start(); QVERIFY(recursiveMutex.tryLock(0)); - QVERIFY(timer.elapsed() < waitTime); + QVERIFY(timer.elapsed() < waitTime + systemTimersResolution); QVERIFY(lockCount.testAndSetRelaxed(0, 1)); QVERIFY(recursiveMutex.tryLock(0)); QVERIFY(lockCount.testAndSetRelaxed(1, 2)); @@ -439,7 +493,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 +545,7 @@ public: void run() { - QTime t; + QElapsedTimer t; t.start(); do { if (mutex.tryLock()) @@ -619,7 +673,7 @@ void tst_QMutex::tryLockNegative() class MoreStressTestThread : public QThread { - QTime t; + QElapsedTimer t; public: static QAtomicInt lockCount; static QAtomicInt sentinel[threadCount]; |