Create a QObject in another thread, retrieve it to the current thread and set a parent = ASSERT failure in Debug on msvc16
-
Hi everyone,
I am just trying to set a parent on a QObject created in another thread (the object is of course moved to the parent thread before), that's all !#include <QApplication> #include <QDebug> #include <QThread> class Thread : public QThread //Just a convenient Class using a lambda { public: Thread::Thread(QObject *parent = nullptr) : QThread(parent){} std::function<void()> todo; protected: virtual void run() override{ todo(); } }; int main(int argc, char *argv[]) { QApplication a(argc, argv); /////////////////////////////////////////////// // Change this flag to switch behaviours bool tryToSetParentInThread = true; /////////////////////////////////////////////// QObject mainObj; QObject *dummy; //Just to get back obj created in thread; Thread thread; thread.todo = [&](){ //QObject *obj = new QObject(&mainObj); //"QObject: Cannot create children for a parent that is in a different thread." ! Of course! // So we try this QObject *obj = new QObject; dummy = obj; qDebug()<<obj->thread(); obj->moveToThread(mainObj.thread()); qDebug()<<obj->thread(); //Check that the Thread affinity change is done if(tryToSetParentInThread) obj->setParent(&mainObj); QObject::connect(obj, &QObject::destroyed, [](){ //Parent mecanism is OK qDebug()<<"Child destroyed"; }); }; thread.start(); thread.wait(); if(!tryToSetParentInThread) dummy->setParent(&mainObj); return 0; //No need for an event loop here }
This example is working fine in release, but if you try to launch this code in debug with mscv16:
Maybe the call of obj->setParent(&mainObj) doesn't like that mainObj is not in the thread calling the method ..? -
You're accessing a QObject and doing a lot of stuff with it inside another thread - that this will crash sooner or later should be clear.
-
You can't set a parent which is not in the same thread.
-
@Christian-Ehrlicher Of course ! This is not what I am doing here !
The objects have the same thread affinity when I do the setParent()
You can see a commented line of what I am not doing :-D
-
But you're calling the QObject function from another thread which creates an event in the thread where the object does not life in and therefore the assert
-
@Christian-Ehrlicher That is what I supposed "Maybe the call of obj->setParent(&mainObj) doesn't like that mainObj is not in the thread calling the method ..?"
Soobj1->setParent(obj2)
triggers a sendEvent() ? No mention of that in documentation !!
But if it does, it is weird to not check thread affinity and replace the sendEvent() by a postEvent() :-(Edit :
@Christian-Ehrlicher This really ugly but indeed it worked, I had to force the execution of the setParent() in the main thread via a queued connection in the thread. And of course start the eventloop in the main thread#include <QApplication> #include <QDebug> #include <QThread> #include <QTimer> class Thread : public QThread //Just a convenient Class using a lambda { public: Thread::Thread(QObject *parent = nullptr) : QThread(parent){} std::function<void()> todo; protected: virtual void run() override{ todo(); } }; int main(int argc, char *argv[]) { QApplication a(argc, argv); /////////////////////////////////////////////// // Change this flag to switch behaviours bool tryToSetParentInThread = true; /////////////////////////////////////////////// QObject mainObj; Thread thread; thread.todo = [&](){ //QObject *obj = new QObject(&mainObj); //"QObject: Cannot create children for a parent that is in a different thread." ! Of course! // So we try this QObject *obj = new QObject; qDebug()<<obj->thread(); obj->moveToThread(mainObj.thread()); qDebug()<<obj->thread(); //Check that the Thread affinity change is done QObject::connect(QThread::currentThread(), &QThread::finished, obj, [obj,&mainObj](){ qDebug()<<"Most fucked up code I wrote"; obj->setParent(&mainObj); }); QObject::connect(obj, &QObject::destroyed, [](){ //Parent mecanism is OK qDebug()<<"Child destroyed"; }); }; thread.start(); thread.wait(); QTimer::singleShot(0,[](){ qApp->quit(); }); return a.exec(); //Add an event loop for the connect (queued) from the thread -> setParent() triggers a SendEvent() in the main thread where the two object now live }
-
You're accessing a QObject and doing a lot of stuff with it inside another thread - that this will crash sooner or later should be clear.
-
So I debugged qt lib, the problem comes from setParent() doing a sendEvent() without checking the thread affinity. This is simply a bug.
Depending on the calling thread it should make a postevent() instead
I ve just replaceobj->setParent(&mainObj);
byQMetaObject::invokeMethod(&mainObj, [&mainObj, obj](){ obj->setParent(&mainObj); });
The invokeMethod() execute setParent in the correct.
-
@NiHoTleHot said in Create a QObject in another thread, retrieve it to the current thread and set a parent = ASSERT failure in Debug on msvc16:
This is simply a bug.
No, you're modifying an object which lives in another thread - it may work or it may crash.