Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-135640

Data race in QPointer (QtSharedPointer::ExternalRefCountData::getAndRef)

    XMLWordPrintable

Details

    • Linux/X11
    • 253f34082 (dev), df454f51f (6.9), 46d9b07de (6.8), 6f39ff2c7 (tqtc/lts-6.5)

    Description

      WARNING: ThreadSanitizer: data race (pid=391944)
        Atomic write of size 4 at 0x72040062aba0 by thread T103:
          #0 std::__atomic_base<int>::fetch_add(int, std::memory_order) /usr/include/c++/14/bits/atomic_base.h:631 (libQt6Core_tsan.so.6+0xf1f24)
          #1 bool QAtomicOps<int>::ref<int>(std::atomic<int>&) qtbase/src/corelib/thread/qatomic_cxx11.h:259 (libQt6Core_tsan.so.6+0xf1f24)
          #2 QBasicAtomicInteger<int>::ref() qtbase/src/corelib/thread/qbasicatomic.h:47 (libQt6Core_tsan.so.6+0xef5f6)
          #3 QtSharedPointer::ExternalRefCountData::getAndRef(QObject const*) qtbase/src/corelib/tools/qsharedpointer.cpp:1536 (libQt6Core_tsan.so.6+0x5f5cf2)
          #4 QWeakPointer<QObject>::QWeakPointer<QObject, true>(QObject*, bool) inst-tsan/include/QtCore/qsharedpointer_impl.h:773
          #5 QWeakPointer<QObject>& QWeakPointer<QObject>::assign<QObject>(QObject*) inst-tsan/include/QtCore/qsharedpointer_impl.h:768
      
        Previous write of size 8 at 0x72040062aba0 by thread T164:
          #0 operator new(unsigned long) <null> (libtsan.so.2+0xa75e1)
          #1 QtSharedPointer::ExternalRefCountData::getAndRef(QObject const*) qtbase/src/corelib/tools/qsharedpointer.cpp:1541 (libQt6Core_tsan.so.6+0x5f5d13)
          #2 QWeakPointer<QObject>::QWeakPointer<QObject, true>(QObject*, bool) inst-tsan/include/QtCore/qsharedpointer_impl.h:773
          #3 QWeakPointer<QObject>& QWeakPointer<QObject>::assign<QObject>(QObject*) inst-tsan/include/QtCore/qsharedpointer_impl.h:768
      
      1528│ QtSharedPointer::ExternalRefCountData *QtSharedPointer::ExternalRefCountData::getAndRef(const QObject *obj)
      1529│ {
      1530│     Q_ASSERT(obj);
      1531│     QObjectPrivate *d = QObjectPrivate::get(const_cast<QObject *>(obj));
      1532│     Q_ASSERT_X(!d->wasDeleted, "QWeakPointer", "Detected QWeakPointer creation in a QObject being deleted");
      1533│
      1534│     ExternalRefCountData *that = d->sharedRefcount.loadRelaxed();
      1535│     if (that) {
      1536├───────> that->weakref.ref();
      1537│         return that;
      1538│     }
      1539│
      1540│     // we can create the refcount data because it doesn't exist
      1541│     ExternalRefCountData *x = ::new ExternalRefCountData(Qt::Uninitialized);
      1542│     x->strongref.storeRelaxed(-1);
      1543│     x->weakref.storeRelaxed(2);  // the QWeakPointer that called us plus the QObject itself
      1544│
      1545│     ExternalRefCountData *ret;
      1546│     if (d->sharedRefcount.testAndSetOrdered(nullptr, x, ret)) {     // ought to be release+acquire; this is acq_rel+acquire
      1547│         ret = x;
      1548│     } else {
      1549│         // ~ExternalRefCountData has a Q_ASSERT, so we use this trick to
      1550│         // only execute this if Q_ASSERTs are enabled
      1551│         Q_ASSERT((x->weakref.storeRelaxed(0), true));
      1552│         ::delete x;
      1553│         ret->weakref.ref();
      1554│     }
      1555│     return ret;
      1556│ }
      

      TSAN is saying the that->weakref.ref() on line 1536 races with the initial state of weakref in new on line 1541. I am very puzzled by that because it got "replaced" with the storeRelaxed(2) on line 1543. I (or TSAN) must be missing something....

      Attachments

        For Gerrit Dashboard: QTBUG-135640
        # Subject Branch Project Status CR V

        Activity

          People

            dfaure_kdab David Faure
            dfaure_kdab David Faure
            Votes:
            1 Vote for this issue
            Watchers:
            5 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Gerrit Reviews

                There are no open Gerrit changes