diff options
| -rw-r--r-- | src/corelib/thread/qreadwritelock.cpp | 8 | ||||
| -rw-r--r-- | src/corelib/tools/qeasingcurve.cpp | 4 | ||||
| -rw-r--r-- | src/widgets/accessible/qaccessiblecolorwell.cpp | 5 | ||||
| -rw-r--r-- | tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp | 106 |
4 files changed, 115 insertions, 8 deletions
diff --git a/src/corelib/thread/qreadwritelock.cpp b/src/corelib/thread/qreadwritelock.cpp index 96e35dcb965..2a1af2315ca 100644 --- a/src/corelib/thread/qreadwritelock.cpp +++ b/src/corelib/thread/qreadwritelock.cpp @@ -234,14 +234,14 @@ QBasicReadWriteLock::contendedTryLockForRead(QDeadlineTimer timeout, void *dd) return d->recursiveLockForRead(timeout); auto lock = qt_unique_lock(d->mutex); - if (d != d_ptr.loadRelaxed()) { + if (QReadWriteLockPrivate *dd = d_ptr.loadAcquire(); d != dd) { // d_ptr has changed: this QReadWriteLock was unlocked before we had // time to lock d->mutex. // We are holding a lock to a mutex within a QReadWriteLockPrivate // that is already released (or even is already re-used). That's ok // because the QFreeList never frees them. // Just unlock d->mutex (at the end of the scope) and retry. - d = d_ptr.loadAcquire(); + d = dd; continue; } return d->lockForRead(lock, timeout); @@ -340,11 +340,11 @@ QBasicReadWriteLock::contendedTryLockForWrite(QDeadlineTimer timeout, void *dd) return d->recursiveLockForWrite(timeout); auto lock = qt_unique_lock(d->mutex); - if (d != d_ptr.loadRelaxed()) { + if (QReadWriteLockPrivate *dd = d_ptr.loadAcquire(); d != dd) { // The mutex was unlocked before we had time to lock the mutex. // We are holding to a mutex within a QReadWriteLockPrivate that is already released // (or even is already re-used) but that's ok because the QFreeList never frees them. - d = d_ptr.loadAcquire(); + d = dd; continue; } return d->lockForWrite(lock, timeout); diff --git a/src/corelib/tools/qeasingcurve.cpp b/src/corelib/tools/qeasingcurve.cpp index de68a0042ac..ce35e8ccffe 100644 --- a/src/corelib/tools/qeasingcurve.cpp +++ b/src/corelib/tools/qeasingcurve.cpp @@ -332,8 +332,6 @@ struct TCBPoint qreal _c; qreal _b; - TCBPoint() {} - TCBPoint(QPointF point, qreal t, qreal c, qreal b) : _point(point), _t(t), _c(c), _b(b) {} bool operator==(const TCBPoint &other) const { @@ -1381,7 +1379,7 @@ void QEasingCurve::addTCBSegment(const QPointF &nextPoint, qreal t, qreal c, qre if (!d_ptr->config) d_ptr->config = curveToFunctionObject(d_ptr->type); - d_ptr->config->_tcbPoints.append(TCBPoint(nextPoint, t, c, b)); + d_ptr->config->_tcbPoints.append(TCBPoint{nextPoint, t, c, b}); if (nextPoint == QPointF(1.0, 1.0)) { d_ptr->config->_bezierCurves = tcbToBezier(d_ptr->config->_tcbPoints); diff --git a/src/widgets/accessible/qaccessiblecolorwell.cpp b/src/widgets/accessible/qaccessiblecolorwell.cpp index ca08e511e9a..64fcd2a7fd1 100644 --- a/src/widgets/accessible/qaccessiblecolorwell.cpp +++ b/src/widgets/accessible/qaccessiblecolorwell.cpp @@ -3,6 +3,8 @@ #include "private/qaccessiblecolorwell_p.h" +#include <QtCore/qcoreapplication.h> + QT_REQUIRE_CONFIG(accessibility); #if QT_CONFIG(colordialog) @@ -14,6 +16,7 @@ class QAccessibleColorWellItem : public QAccessibleInterface { QAccessibleColorWell *m_parent; + Q_DECLARE_TR_FUNCTIONS(QAccessibleColorWellItem) public: QAccessibleColorWellItem(QAccessibleColorWell *parent); @@ -79,7 +82,7 @@ QString QAccessibleColorWellItem::text(QAccessible::Text t) const if (t == QAccessible::Name) { QRgb color = m_parent->colorWell()->rgbValues()[m_parent->indexOfChild(this)]; //: Color specified via its 3 RGB components (red, green, blue) - return QObject::tr("RGB %1, %2, %3") + return tr("RGB %1, %2, %3") .arg(QString::number(qRed(color)), QString::number(qGreen(color)), QString::number(qBlue(color))); } diff --git a/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp b/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp index 86dfa5faffc..4c089091f8d 100644 --- a/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp +++ b/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp @@ -57,6 +57,7 @@ private slots: void multipleReadersLoop(); void multipleWritersLoop(); void multipleReadersWritersLoop(); + void heavyLoadLocks(); void countingTest(); void limitedReaders(); void deleteOnUnlock(); @@ -603,6 +604,111 @@ public: } }; +class HeavyLoadLockThread : public QThread +{ +public: + QReadWriteLock &testRwlock; + const qsizetype iterations; + const int numThreads; + inline HeavyLoadLockThread(QReadWriteLock &l, qsizetype iters, int numThreads, QVector<QAtomicInt *> &counters): + testRwlock(l), + iterations(iters), + numThreads(numThreads), + counters(counters) + { } + +private: + QVector<QAtomicInt *> &counters; + QAtomicInt *getCounter(qsizetype index) + { + QReadLocker locker(&testRwlock); + /* + The index is increased monotonically, so the index + being requested should be always within or at the end of the + counters vector. + */ + Q_ASSERT(index <= counters.size()); + if (counters.size() <= index || counters[index] == nullptr) { + locker.unlock(); + QWriteLocker wlocker(&testRwlock); + if (counters.size() <= index) + counters.resize(index + 1, nullptr); + if (counters[index] == nullptr) + counters[index] = new QAtomicInt(0); + return counters[index]; + } + return counters[index]; + } + void releaseCounter(qsizetype index) + { + QWriteLocker locker(&testRwlock); + delete counters[index]; + counters[index] = nullptr; + } + +public: + void run() override + { + for (qsizetype i = 0; i < iterations; ++i) { + QAtomicInt *counter = getCounter(i); + /* + Here each counter is accessed by each thread + and increaed only once. As a result, when the + counter reaches numThreads, i.e. the fetched + value before the increment is numThreads-1, + we know all threads have accessed this counter + and we can delete it safely. + */ + int prev = counter->fetchAndAddRelaxed(1); + if (prev == numThreads - 1) { +#ifdef QT_BUILDING_UNDER_TSAN + /* + Under TSAN, deleting and freeing an object + will trigger a write operation on the memory + of the object. Since we used fetchAndAddRelaxed + to update the counter, TSAN will report a data + race when deleting the counter here. To avoid + the false positive, we simply reset the counter + to 0 here, with ordered semantics to establish + the sequence to ensure the the free-ing option + happens after all fetchAndAddRelaxed operations + in other threads. + + When not building under TSAN, deleting the counter + will not result in any data read or written to the + memory region of the counter, so no data race will + happen. + */ + counter->fetchAndStoreOrdered(0); +#endif + releaseCounter(i); + } + } + } +}; + +/* + Multiple threads racing acquiring and releasing + locks on the same indices. +*/ + +void tst_QReadWriteLock::heavyLoadLocks() +{ + constexpr qsizetype iterations = 65536 * 4; + constexpr int numThreads = 8; + QVector<QAtomicInt *> counters; + QReadWriteLock testLock; + std::array<std::unique_ptr<HeavyLoadLockThread>, numThreads> threads; + for (auto &thread : threads) + thread = std::make_unique<HeavyLoadLockThread>(testLock, iterations, numThreads, counters); + for (auto &thread : threads) + thread->start(); + for (auto &thread : threads) + thread->wait(); + QVERIFY(counters.size() == iterations); + for (qsizetype i = 0; i < iterations; ++i) + QVERIFY(counters[i] == nullptr); +} /* A writer acquires a read-lock, a reader locks |
