tst_QThread: add a test for multiple threads wait()ing for one
There's nothing in our documentation saying this is permitted, but we have specific code for it on Windows and it works on Unix because we just wait on a QWaitCondition. Change-Id: Id6331fa9aad473cb4f35fffdf8bb04d9a34cd108 Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io> Reviewed-by: Ivan Solovev <ivan.solovev@qt.io> Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> (cherry picked from commit 5b5297fe87859f59a7aaf5e86a8915c00714fefa) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
c68c0fb64c
commit
e080017c72
@ -81,6 +81,8 @@ private slots:
|
||||
void connectThreadFinishedSignalToObjectDeleteLaterSlot();
|
||||
void wait2();
|
||||
void wait3_slowDestructor();
|
||||
void multiThreadWait_data();
|
||||
void multiThreadWait();
|
||||
void destroyFinishRace();
|
||||
void startFinishRace();
|
||||
void startAndQuitCustomEventLoop();
|
||||
@ -1197,6 +1199,113 @@ void tst_QThread::wait3_slowDestructor()
|
||||
QVERIFY(thread.wait(one_minute));
|
||||
}
|
||||
|
||||
void tst_QThread::multiThreadWait_data()
|
||||
{
|
||||
QTest::addColumn<QList<int>>("deadlines");
|
||||
auto addRow = [](auto &&... list) {
|
||||
static_assert(sizeof...(list) <= 5,
|
||||
"Limited by std::array in tst_QThread::multiThreadWait()");
|
||||
QList<int> deadlines = { std::move(list)... };
|
||||
QByteArrayList name;
|
||||
for (int value : deadlines) {
|
||||
if (value < 0)
|
||||
name.append("Forever");
|
||||
else
|
||||
name.append(QByteArray::number(value));
|
||||
}
|
||||
QTest::newRow(name.join('-').constData()) << deadlines;
|
||||
};
|
||||
|
||||
addRow(0, 0);
|
||||
addRow(0, 0, 0, 0, 0);
|
||||
addRow(-1, -1);
|
||||
addRow(-1, -1, -1, -1, -1);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
void tst_QThread::multiThreadWait()
|
||||
{
|
||||
class TargetThread : public QThread {
|
||||
public:
|
||||
QSemaphore sync;
|
||||
void run() override
|
||||
{
|
||||
sync.acquire();
|
||||
msleep(Waiting_Thread::WaitTime);
|
||||
}
|
||||
};
|
||||
|
||||
class WaiterThread : public QThread {
|
||||
public:
|
||||
QSemaphore *startSema, *endSema;
|
||||
QThread *target;
|
||||
QDeadlineTimer deadline;
|
||||
QElapsedTimer::Duration waitedDuration = {};
|
||||
int result = -1;
|
||||
void run() override
|
||||
{
|
||||
QElapsedTimer elapsed;
|
||||
elapsed.start();
|
||||
startSema->acquire();
|
||||
result = target->wait(deadline);
|
||||
waitedDuration = elapsed.durationElapsed();
|
||||
endSema->release();
|
||||
}
|
||||
};
|
||||
|
||||
QFETCH(QList<int>, deadlines);
|
||||
TargetThread target;
|
||||
target.start();
|
||||
|
||||
QSemaphore startSema, endSema;
|
||||
std::array<std::unique_ptr<WaiterThread>, 5> threads; // 5 threads is enough
|
||||
for (int i = 0; i < deadlines.size(); ++i) {
|
||||
threads[i] = std::make_unique<WaiterThread>();
|
||||
threads[i]->startSema = &startSema;
|
||||
threads[i]->endSema = &endSema;
|
||||
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
|
||||
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));
|
||||
|
||||
// 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) {
|
||||
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);
|
||||
}
|
||||
printI.dismiss();
|
||||
threads[i].reset();
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QThread::destroyFinishRace()
|
||||
{
|
||||
class Thread : public QThread { void run() override {} };
|
||||
|
Loading…
x
Reference in New Issue
Block a user