QPointer: fix data race on ExternalRefCountData

The creation of the ExternalRefCountData was published with
testAndSetOrdered() but the loadRelaxed() would only load the pointer
value, not the effects of the constructor.

Pick-to: 6.9 6.8 6.5
Fixes: QTBUG-135640
Change-Id: I3acbc51e763e8a291be3f7036e0d9cd3965a2ce8
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
This commit is contained in:
David Faure 2025-04-09 14:24:04 +02:00
parent 22d4f117bd
commit 253f34082f
2 changed files with 38 additions and 1 deletions

View File

@ -1531,7 +1531,7 @@ QtSharedPointer::ExternalRefCountData *QtSharedPointer::ExternalRefCountData::ge
QObjectPrivate *d = QObjectPrivate::get(const_cast<QObject *>(obj));
Q_ASSERT_X(!d->wasDeleted, "QWeakPointer", "Detected QWeakPointer creation in a QObject being deleted");
ExternalRefCountData *that = d->sharedRefcount.loadRelaxed();
ExternalRefCountData *that = d->sharedRefcount.loadAcquire();
if (that) {
that->weakref.ref();
return that;

View File

@ -6,6 +6,7 @@
#include <QtCore/private/qobject_p.h>
#include <QRunnable>
#include <QSemaphore>
#include <QThreadPool>
#include <QPointer>
@ -34,6 +35,7 @@ private slots:
void disconnect();
void castDuringDestruction();
void threadSafety();
void raceCondition();
void qvariantCast();
void constPointer();
@ -529,6 +531,41 @@ void tst_QPointer::threadSafety()
owner.wait();
}
void tst_QPointer::raceCondition()
{
const int NUM_THREADS = 20;
const int ITERATIONS_PER_THREAD = 10;
QSemaphore startSemaphore;
QObject targetObject;
std::vector<std::unique_ptr<QThread>> threads;
threads.reserve(NUM_THREADS);
for (int i = 0; i < NUM_THREADS; ++i) {
QThread *thread =
QThread::create([&startSemaphore, &targetObject, ITERATIONS_PER_THREAD]() {
startSemaphore.acquire();
for (int j = 0; j < ITERATIONS_PER_THREAD; ++j) {
QPointer<QObject> pointer(&targetObject);
Q_UNUSED(pointer);
}
});
threads.emplace_back(thread);
thread->start();
}
QTest::qWait(100);
startSemaphore.release(NUM_THREADS);
for (const auto &thread : threads) {
QVERIFY(thread->wait(30000));
}
}
void tst_QPointer::qvariantCast()
{
QFile file;