Details
-
Bug
-
Resolution: Out of scope
-
Not Evaluated
-
None
-
5.14.2
-
None
Description
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.