diff options
author | Brennan Vincent <brennan@umanwizard.com> | 2025-07-21 14:23:43 -0700 |
---|---|---|
committer | Andreas Enge <andreas@enge.fr> | 2025-07-22 10:49:07 +0200 |
commit | e7a87d59b84f20307f22f6878b39e0210496104e (patch) | |
tree | 0f6b947ede1fcad0d932fe61cca966aa58c78a5e /gnu/packages/patches | |
parent | 355b6125c02790bf24a39a90e155e68f577d4982 (diff) |
gnu: qtbase: Backport fix for flaky test.
* gnu/packages/qt.scm (qtbase)[#:phases]: Add 'patch-aarch64-tests, which
backports an upstream commit improving a flaky test.
* gnu/packages/patches/qtbase-fix-thread-test.patch: New file.
* gnu/local.mk (dist_patch_DATA): Register patch.
Change-Id: Ibf9b95d7225ed42c9edad03a550d1c6e6f86be54
Diffstat (limited to 'gnu/packages/patches')
-rw-r--r-- | gnu/packages/patches/qtbase-fix-thread-test.patch | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/gnu/packages/patches/qtbase-fix-thread-test.patch b/gnu/packages/patches/qtbase-fix-thread-test.patch new file mode 100644 index 0000000000..d54386ccc7 --- /dev/null +++ b/gnu/packages/patches/qtbase-fix-thread-test.patch @@ -0,0 +1,154 @@ +From 2bce75a6b53cccbf9c813581b64eea87f3ab55fc Mon Sep 17 00:00:00 2001 +From: Thiago Macieira <thiago.macieira@intel.com> +Date: Fri, 1 Nov 2024 11:59:10 -0700 +Subject: [PATCH] tst_QThread: improve test on multiple threads waiting on + wait() + +Amends commit 5b5297fe87859f59a7aaf5e86a8915c00714fefa to make it more +reliable against CI timeouts and flakiness. Now, we wait for the threads +that are expected to timeout to actually timeout before releasing the +target thread they were waiting on. It should be impossible now for this +to go wrong. There should also be no problem of their handing off to a +thread that will wait forever. + +I've added two more tests that could, possibly, have a problem in the +CI: when timing-out threads hand off to a timed wait that is expected to +succeed (the > 1s wait cases). Though this should be a rare occurrence, +if ever: the target thread's runtime is the longest of the timing out +threads' wait, plus 5 ms. That is 30 ms in the examples I wrote, so the +additional extra time they're waiting on should be more than enough. + +An extra benefit is that this test now runs much faster, at 10 to 60 ms +per test row, instead of 800 ms previously. The drawback is that a +failure condition is likely going to be noticed by the QSemaphores +deadlocking. + +Change-Id: I360d7e9c7accc1216291fffd743c88a362cf66ac +Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> +--- + .../corelib/thread/qthread/tst_qthread.cpp | 63 +++++++++++++------ + 1 file changed, 43 insertions(+), 20 deletions(-) + +diff --git a/tests/auto/corelib/thread/qthread/tst_qthread.cpp b/tests/auto/corelib/thread/qthread/tst_qthread.cpp +index 99602098fac..beb843b0d8c 100644 +--- a/tests/auto/corelib/thread/qthread/tst_qthread.cpp ++++ b/tests/auto/corelib/thread/qthread/tst_qthread.cpp +@@ -36,6 +36,8 @@ + #include <exception> + #endif + ++#include <thread> ++ + #include <QtTest/private/qemulationdetector_p.h> + + using namespace std::chrono_literals; +@@ -1226,21 +1228,28 @@ void tst_QThread::multiThreadWait_data() + // this is probably too fast and the Forever gets in too quickly + addRow(0, -1); + +- addRow(100, -1); +- addRow(100, 200, -1); +- addRow(200, 100, -1); +- addRow(-1, 100, 100, 100); ++ // any time below 100ms (see below) is expected to timeout ++ addRow(25, -1); ++ addRow(25, 50, -1); ++ addRow(50, 25, -1); ++ addRow(-1, 25, 25, 25); ++ addRow(25, 2000); ++ addRow(25, 2000, 25, -1); + } + + void tst_QThread::multiThreadWait() + { ++ static constexpr auto TimeoutThreshold = 100ms; ++ auto isExpectedToTimeout = [](unsigned value) { ++ return value < TimeoutThreshold.count(); ++ }; ++ + class TargetThread : public QThread { + public: + QSemaphore sync; + void run() override + { + sync.acquire(); +- msleep(Waiting_Thread::WaitTime); + } + }; + +@@ -1265,43 +1274,57 @@ void tst_QThread::multiThreadWait() + QFETCH(QList<int>, deadlines); + TargetThread target; + target.start(); ++ QElapsedTimer elapsedTimer; ++ elapsedTimer.start(); + +- QSemaphore startSema, endSema; ++ // we use a QSemaphore to wait on the WaiterThread::run() instead of ++ // QThread::wait() so it's easier to debug when the latter has a problem ++ QSemaphore startSema, timeoutSema, successSema; + std::array<std::unique_ptr<WaiterThread>, 5> threads; // 5 threads is enough ++ int expectedTimeoutCount = 0; + for (int i = 0; i < deadlines.size(); ++i) { + threads[i] = std::make_unique<WaiterThread>(); + threads[i]->startSema = &startSema; +- threads[i]->endSema = &endSema; ++ if (isExpectedToTimeout(deadlines.at(i))) { ++ ++expectedTimeoutCount; ++ threads[i]->endSema = &timeoutSema; ++ } else { ++ threads[i]->endSema = &successSema; ++ } + threads[i]->target = ⌖ + threads[i]->deadline = QDeadlineTimer(deadlines.at(i)); + threads[i]->start(); + } + +- // release the waiting threads first, then the target thread they're waiting on ++ // release the waiting threads first, so they begin waiting + startSema.release(deadlines.size()); +- target.sync.release(); + +- // wait for our waiting threads on a semaphore instead of QThread::wait() +- // to make debugging easier +- QVERIFY(endSema.tryAcquire(deadlines.size(), QDeadlineTimer::Forever)); ++ // then wait for the threads that are expected to timeout to do so ++ QVERIFY(timeoutSema.tryAcquire(expectedTimeoutCount, QDeadlineTimer::Forever)); ++ ++ // compute the elapsed time for timing comparisons ++ std::this_thread::sleep_for(5ms); // short, but long enough to avoid rounding errors ++ auto elapsed = elapsedTimer.durationElapsed(); ++ std::this_thread::sleep_for(5ms); ++ ++ // cause the target thread to exit, so the successful threads do succeed ++ target.sync.release(); ++ int expectedSuccessCount = deadlines.size() - expectedTimeoutCount; ++ QVERIFY(successSema.tryAcquire(expectedSuccessCount, QDeadlineTimer::Forever)); + + // wait for all the threads to end, before QVERIFY/QCOMPAREs + for (int i = 0; i < deadlines.size(); ++i) + threads[i]->wait(); + target.wait(); + +- std::chrono::milliseconds expectedDuration{Waiting_Thread::WaitTime}; + for (int i = 0; i < deadlines.size(); ++i) { + auto printI = qScopeGuard([i] { qWarning("i = %i", i); }); +- if (unsigned(deadlines.at(i)) < Waiting_Thread::WaitTime / 2) { ++ if (isExpectedToTimeout(deadlines.at(i))) { ++ QCOMPARE_LT(threads[i]->waitedDuration, elapsed); + QCOMPARE(threads[i]->result, false); +- QCOMPARE_LT(threads[i]->waitedDuration, expectedDuration); +- } else if (unsigned(deadlines.at(i)) > Waiting_Thread::WaitTime * 3 / 2) { +- QCOMPARE(threads[i]->result, true); +- QCOMPARE_GE(threads[i]->waitedDuration, expectedDuration); + } else { +- qWarning("Wait time %i (index %i) is too close to the target time; test would be flaky", +- deadlines.at(i), i); ++ QCOMPARE_GE(threads[i]->waitedDuration, elapsed); ++ QCOMPARE(threads[i]->result, true); + } + printI.dismiss(); + threads[i].reset(); +-- +2.50.1 + |