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

TSan reports data race when using a slot/functor

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Out of scope
    • Icon: Not Evaluated Not Evaluated
    • None
    • 5.14.2
    • Core: Object Model
    • None

      I'm seeing lots of TSan violations in an app, and I boiled things down to something that shows the data race with just the following:

      #include <QCoreApplication>
      #include <QObject>
      #include <QThread>
      
      class MyObject : public QObject {
        Q_OBJECT
      
      public:
        MyObject() : QObject(nullptr) {}
        ~MyObject() = default;
      
      public slots:
        void doSomething() {}
      };
      
      #include "main.moc"
      
      int main(int argc, char *argv[])
      {
        QCoreApplication app(argc, argv);
      
        QThread thread;
        thread.start();
      
        MyObject objectOnThread;
        objectOnThread.moveToThread(&thread);
      
        QMetaObject::invokeMethod(&objectOnThread, &MyObject::doSomething);
      
        return app.exec();
      }

      If you run the above then you get something like this:

      ==================
      WARNING: ThreadSanitizer: data race (pid=64001)
        Read of size 8 at 0x7b0800005430 by thread T4:
          #0 QtPrivate::QSlotObject<void (MyObject::*)(), QtPrivate::List<>, void>::impl(int, QtPrivate::QSlotObjectBase*, QObject*, void**, bool*) qobjectdefs_impl.h:418 (TSanSlots:x86_64+0x100004390)
          #1 QObject::event(QEvent*) qobject.cpp:1339 (QtCore:x86_64+0x220cfe)
      
      
        Previous write of size 8 at 0x7b0800005430 by main thread:
          #0 QtPrivate::QSlotObject<void (MyObject::*)(), QtPrivate::List<>, void>::QSlotObject(void (MyObject::*)()) qobjectdefs_impl.h:427 (TSanSlots:x86_64+0x1000042ae)
          #1 QtPrivate::QSlotObject<void (MyObject::*)(), QtPrivate::List<>, void>::QSlotObject(void (MyObject::*)()) qobjectdefs_impl.h:427 (TSanSlots:x86_64+0x1000041f8)
          #2 std::__1::enable_if<((QtPrivate::FunctionPointer<void (MyObject::*)()>::IsPointerToMemberFunction) && (!(std::is_convertible<void (MyObject::*)(), char const*>::value))) && ((QtPrivate::FunctionPointer<void (MyObject::*)()>::ArgumentCount) == (0)), bool>::type QMetaObject::invokeMethod<void (MyObject::*)()>(QtPrivate::FunctionPointer<void (MyObject::*)()>::Object*, void (MyObject::*)(), Qt::ConnectionType, QtPrivate::FunctionPointer<void (MyObject::*)()>::ReturnType*) qobjectdefs.h:481 (TSanSlots:x86_64+0x100003f76)
          #3 main main.cpp:28 (TSanSlots:x86_64+0x100003d7b)
      
      
        Location is heap block of size 32 at 0x7b0800005420 allocated by main thread:
          #0 operator new(unsigned long) <null>:2672656 (libclang_rt.tsan_osx_dynamic.dylib:x86_64h+0x7596b)
          #1 std::__1::enable_if<((QtPrivate::FunctionPointer<void (MyObject::*)()>::IsPointerToMemberFunction) && (!(std::is_convertible<void (MyObject::*)(), char const*>::value))) && ((QtPrivate::FunctionPointer<void (MyObject::*)()>::ArgumentCount) == (0)), bool>::type QMetaObject::invokeMethod<void (MyObject::*)()>(QtPrivate::FunctionPointer<void (MyObject::*)()>::Object*, void (MyObject::*)(), Qt::ConnectionType, QtPrivate::FunctionPointer<void (MyObject::*)()>::ReturnType*) qobjectdefs.h:481 (TSanSlots:x86_64+0x100003f38)
          #2 main main.cpp:28 (TSanSlots:x86_64+0x100003d7b)
      
      
        Thread T4 (tid=51037285, running) created by main thread at:
          #0 pthread_create <null>:2672704 (libclang_rt.tsan_osx_dynamic.dylib:x86_64h+0x2aacd)
          #1 QThread::start(QThread::Priority) qthread_unix.cpp:716 (QtCore:x86_64+0x23030)
          #2 start <null>:2672704 (libdyld.dylib:x86_64+0x1acc8)
      
      
      SUMMARY: ThreadSanitizer: data race qobjectdefs_impl.h:418 in QtPrivate::QSlotObject<void (MyObject::*)(), QtPrivate::List<>, void>::impl(int, QtPrivate::QSlotObjectBase*, QObject*, void**, bool*)
      ==================
      

       And then there is also this case:

      #include <QCoreApplication>
      #include <QObject>
      #include <QThread>
      
      int main(int argc, char *argv[]) {
        QCoreApplication app(argc, argv);
      
        QThread thread;
        thread.start();
      
        QObject objectOnThread;
        objectOnThread.moveToThread(&thread);
      
        auto ptr = std::make_shared<int>(1);
        QMetaObject::invokeMethod(&objectOnThread, [ptr]() {});
      
        return app.exec();
      }

      Which would give the following:

      ==================
      WARNING: ThreadSanitizer: data race (pid=43550)
        Read of size 8 at 0x7b08000055d8 by thread T4:
          #0 std::__1::shared_ptr<int>::~shared_ptr() memory:4520 (TSanSlots:x86_64+0x100004c79)
          #1 std::__1::shared_ptr<int>::~shared_ptr() memory:4519 (TSanSlots:x86_64+0x1000029e8)
          #2 main::$_0::~$_0() main.cpp:16 (TSanSlots:x86_64+0x100002a28)
          #3 main::$_0::~$_0() main.cpp:16 (TSanSlots:x86_64+0x1000029a8)
          #4 QtPrivate::QFunctorSlotObject<main::$_0, 0, QtPrivate::List<>, void>::~QFunctorSlotObject() qobjectdefs_impl.h:432 (TSanSlots:x86_64+0x100005579)
          #5 QtPrivate::QFunctorSlotObject<main::$_0, 0, QtPrivate::List<>, void>::~QFunctorSlotObject() qobjectdefs_impl.h:432 (TSanSlots:x86_64+0x1000054b8)
          #6 QtPrivate::QFunctorSlotObject<main::$_0, 0, QtPrivate::List<>, void>::impl(int, QtPrivate::QSlotObjectBase*, QObject*, void**, bool*) qobjectdefs_impl.h:440 (TSanSlots:x86_64+0x1000053b4)
          #7 QMetaCallEvent::~QMetaCallEvent() qobject.cpp:610 (QtCore:x86_64+0x21f2f4)
      
      
        Previous write of size 8 at 0x7b08000055d8 by main thread:
          #0 std::__1::shared_ptr<int>::shared_ptr(std::__1::shared_ptr<int>&&) memory:4216 (TSanSlots:x86_64+0x100005241)
          #1 std::__1::shared_ptr<int>::shared_ptr(std::__1::shared_ptr<int>&&) memory:4217 (TSanSlots:x86_64+0x100005188)
          #2 main::$_0::$_0($_0&&) main.cpp:16 (TSanSlots:x86_64+0x100005138)
          #3 main::$_0::$_0($_0&&) main.cpp:16 (TSanSlots:x86_64+0x100005088)
          #4 QtPrivate::QFunctorSlotObject<main::$_0, 0, QtPrivate::List<>, void>::QFunctorSlotObject(main::$_0) qobjectdefs_impl.h:451 (TSanSlots:x86_64+0x100005300)
          #5 QtPrivate::QFunctorSlotObject<main::$_0, 0, QtPrivate::List<>, void>::QFunctorSlotObject(main::$_0) qobjectdefs_impl.h:451 (TSanSlots:x86_64+0x1000050d0)
          #6 std::__1::enable_if<((!(QtPrivate::FunctionPointer<main::$_0>::IsPointerToMemberFunction)) && ((QtPrivate::FunctionPointer<main::$_0>::ArgumentCount) == (-(1)))) && (!(std::is_convertible<main::$_0, char const*>::value)), bool>::type QMetaObject::invokeMethod<main::$_0>(QObject*, main::$_0, Qt::ConnectionType, decltype(fp0())*) qobjectdefs.h:526 (TSanSlots:x86_64+0x10000285e)
          #7 main main.cpp:16 (TSanSlots:x86_64+0x100002631)
      
      
        Location is heap block of size 32 at 0x7b08000055c0 allocated by main thread:
          #0 operator new(unsigned long) <null>:2673072 (libclang_rt.tsan_osx_dynamic.dylib:x86_64h+0x7596b)
          #1 std::__1::enable_if<((!(QtPrivate::FunctionPointer<main::$_0>::IsPointerToMemberFunction)) && ((QtPrivate::FunctionPointer<main::$_0>::ArgumentCount) == (-(1)))) && (!(std::is_convertible<main::$_0, char const*>::value)), bool>::type QMetaObject::invokeMethod<main::$_0>(QObject*, main::$_0, Qt::ConnectionType, decltype(fp0())*) qobjectdefs.h:526 (TSanSlots:x86_64+0x100002820)
          #2 main main.cpp:16 (TSanSlots:x86_64+0x100002631)
      
      
        Thread T4 (tid=50888400, running) created by main thread at:
          #0 pthread_create <null>:2673120 (libclang_rt.tsan_osx_dynamic.dylib:x86_64h+0x2aacd)
          #1 QThread::start(QThread::Priority) qthread_unix.cpp:716 (QtCore:x86_64+0x23030)
          #2 start <null>:2673120 (libdyld.dylib:x86_64+0x1acc8)
      
      
      SUMMARY: ThreadSanitizer: data race memory:4520 in std::__1::shared_ptr<int>::~shared_ptr()
      ==================

      I find it hard to believe that this very central part of Qt signals/slots would not be operating correctly, however I also cannot see what stops there being races here.

      I'm going to keep wrapping my head around it, but I figured a bug report would be a good place to start to ask if this is something expected and a false positive.

        1. TSanSlots.zip
          4 kB
        2. TSanSlots.zip
          4 kB
        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

            thiago Thiago Macieira
            mattjgalloway Matt Galloway
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved:

                There are no open Gerrit changes