Planned maintenance: From Sunday 8th December 10:00 CET there will be changes to try and solve the caching issues that have been experienced. If anyone has a problem connecting after this period then please PM @AndyS or any of the moderators.

Passing custom type pointers between QML and threads via signals and slots cause app to crash



  • Hi, as topic partially described, I send pointers to my custom type object, which has 3 members (int, int and QVariant) via sig/slot between the main thread and the worker thread. I use the Controller - Worker approach from qt docs.

    Everything was fine when I have two int members in class, and it started crashing (SIGSEGV) since I have added QVariant member to exchange data between QML and C++.

    I'm guessing that crash is caused by accessing the shared resource (my custom class instance). I tried to add some serialization using QMutex, with and without QMutexLocker, QReadWriteLocker but the result was the same - crash. I also tried to protect the shared resource by applying Monitor construct wiki protecting the data with mutexes, but it also didn't work. At the moment I'm out of ideas and look for any help. Thanks in advance.

    Here is my code (plain text and link to git clone), program will probably crash after clicking the button or few spontaneous clicks. ContractorTask is my custom which I want to send using pointers.

    source:

    // ContractorTask.h
    
    #ifndef TASK_H
    #define TASK_H
    
    #include <QObject>
    #include <QVariant>
    #include <QThread>
    #include <QDebug>
    
    class ContractorTask : public QObject
    {
        Q_OBJECT
    
    public:
        explicit ContractorTask(QObject *parent = nullptr) :
            QObject(parent),
            taskID(-1),
            contractorID(-1),
            data("")
        {
            // ...
        }
    
        Q_INVOKABLE int getTaskID()
        {
            qDebug() << "getTaskID()" << QThread::currentThreadId();
            return taskID;
        }
    
        Q_INVOKABLE void setTaskID(int _ID)
        {
            qDebug() << "setTaskID()" << QThread::currentThreadId();
            taskID = _ID;
        }
    
        Q_INVOKABLE int getConctractorID()
        {
            qDebug() << "getConctractorID()" << QThread::currentThreadId();
            return contractorID;
        }
    
        Q_INVOKABLE void setContractorID(int _ID)
        {
            qDebug() << "setContractorID()" << QThread::currentThreadId();
            contractorID = _ID;
        }
    
        Q_INVOKABLE QVariant getData()
        {
            qDebug() << "getData()" << QThread::currentThreadId();
            return data;
        }
    
        Q_INVOKABLE void setData(QVariant _data)
        {
            qDebug() << "setData()" << QThread::currentThreadId();
            data = _data;
        }
    
    private:
        int taskID;
        int contractorID;
        QVariant data;
    };
    
    #endif // TASK_H
    
    // Contractor.h
    
    #ifndef CONTRACTOR_H
    #define CONTRACTOR_H
    
    #include <QObject>
    #include <QQmlEngine>
    #include <QThread>
    #include <QMutex>
    
    #include <ContractorTask.h>
    
    //#define CONTRACTOR_DEBUG_LOCK
    
    class Contractor : public QObject
    {
        Q_OBJECT
    
    public:
        Contractor(int _ID, QObject* parent = nullptr);
        ~Contractor();
    
        enum class Tasks : int
        {
            NOT_DEFINED = -1,
            Q_LIST_VARIANTS_TO_QML,
            GET_QVARIANT_FROM_QML
        };
    
        Q_ENUMS(Tasks)
    
        inline static void registerTasks()
        {
            qmlRegisterUncreatableType<Contractor>("Tasks", 1, 0, "ContractorTasks", "ContractorTask is uncreatable in QML");
        }
    
        int getID()
        {
            return ID;
        }
    
    public slots:
        void executeTask(ContractorTask* _task);
    
    signals:
        void finished();
        void taskStarted(ContractorTask* _task);
        void taskFinished(ContractorTask* _task);
    
    private:
        int ID;
    
    #ifndef CONTRACTOR_DEBUG_LOCK
        Qt::HANDLE mainThreadID;
    #endif
    };
    #endif // CONTRACTOR_H
    
    //Contractor.cpp
    
    #include "Contractor.h"
    
    #include <QDebug>
    #include <QThread>
    
    Contractor::Contractor(int _ID, QObject *parent) :
        QObject(parent),
        ID(_ID)
    {
    #ifndef CONTRACTOR_DEBUG_LOCK
        mainThreadID = QThread::currentThreadId();
    #endif
    }
    
    Contractor::~Contractor()
    {
        // ...
    
        qDebug() << "Cleaning SerialPortContractor...";
    }
    
    void Contractor::executeTask(ContractorTask* _task)
    {
        emit(taskStarted(_task));
    
        qDebug() << "\tCONTRACTOR STARTED";
    
        int localContractorID = _task->getConctractorID();
    
        if(localContractorID != getID())
        {
            qDebug() << "Not mine ID: " << localContractorID << "discarding";
            return;
        }
    
        qDebug() << "Emiting signal from Contractor";
        emit(taskStarted(_task));
    
        int localTaskID = _task->getTaskID();
        QVariant localData = _task->getData();
    
        QList<QVariant> _params;
        QList<float> _floats;
        _params = localData.value<QList<QVariant>>();
    
        for(auto item : _params)
        {
            _floats << item.toFloat();
        }
    
    #ifndef CONTRACTOR_DEBUG_LOCK
        qDebug() << "Main ThreadID =" << mainThreadID << "my ThreadID =" << QThread::currentThreadId();
    #endif
    
        switch(static_cast<Tasks>(localTaskID))
        {
        case Tasks::Q_LIST_VARIANTS_TO_QML :
        {
            QList<QVariant> _params;
            _params.append(12.5F);
            _params.append(14.36F);
    
            QVariant _data = _params;
    
            _task->setData(_data);
        }
            break;
        case Tasks::GET_QVARIANT_FROM_QML:
        {
            int i = 0;
            for(auto item : _floats)
            {
                qDebug() << "C++ item" << i++ << "value" << item;
            }
        }
            break;
        default:
        {
    #ifndef CONTRACTOR_DEBUG_LOCK
            qDebug() << "Oh... I don't have these one :(";
    #endif
        }
        }
    
        emit(taskFinished(_task));
    
        qDebug() << "\tCONTRACTOR ENDED";
    }
    
    // Controller.h
    
    #ifndef CONTROLLERv2_H
    #define CONTROLLERv2_H
    
    #include <QObject>
    #include <QThread>
    #include <QDebug>
    
    #include <Contractor.h>
    #include <ContractorTask.h>
    
    class Controller : public QObject
    {
        Q_OBJECT
        QThread workerThread;
    
    public:
        Controller()
        {
            Contractor *worker = new Contractor(0);
            worker->moveToThread(&workerThread);
            connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
            connect(this, &Controller::startTask, worker, &Contractor::executeTask);
            connect(worker, &Contractor::taskFinished, this, &Controller::handleResults);
            workerThread.start();
        }
    
        ~Controller()
        {
            workerThread.quit();
            workerThread.wait();
        }
    
    signals:
        void startTask(ContractorTask*);
    
    public slots:
        void handleResults(ContractorTask* _task)
        {
            qDebug() << _task->getTaskID() << _task->getConctractorID() << _task->getData();
        }
    };
    #endif // CONTROLLERv2_H
    
    // main.cpp
    
    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <Controller.h>
    #include <QQmlContext>
    
    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    
        QGuiApplication app(argc, argv);
    
        Contractor::registerTasks();
        Controller TaskController;
        ContractorTask Task;
    
        QQmlApplicationEngine engine;
        engine.rootContext()->setContextProperty("TaskController", &TaskController);
        engine.rootContext()->setContextProperty("Task", &Task);
    
        const QUrl url(QStringLiteral("qrc:/main.qml"));
        QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                         &app, [url](QObject *obj, const QUrl &objUrl) {
            if (!obj && url == objUrl)
                QCoreApplication::exit(-1);
        }, Qt::QueuedConnection);
        engine.load(url);
    
        return app.exec();
    }
    
    // main.qml
    
    import QtQuick 2.12
    import QtQuick.Controls 2.5
    
    import Tasks 1.0
    
    ApplicationWindow {
        id: applicationWindow
        visible: true
        width: 640
        height: 480
        title: qsTr("Test")
    
        Button {
            id: button
            text: qsTr("Button")
            anchors.verticalCenter: parent.verticalCenter
            anchors.horizontalCenter: parent.horizontalCenter
    
            onClicked: {
                var items = [1.25, 2.12345, 2.2, 1.25, 2.12345, 2.2, 1.25, 2.12345, 2.2, 1.25, 2.12345, 2.2]
    
                Task.setData(items)
                runTaskFromQML(ContractorTasks.GET_QVARIANT_FROM_QML, 0)
                runTaskFromQML(ContractorTasks.GET_QVARIANT_FROM_QML, 0)
                runTaskFromQML(ContractorTasks.GET_QVARIANT_FROM_QML, 0)
                runTaskFromQML(ContractorTasks.GET_QVARIANT_FROM_QML, 0)
            }
        }
    
        function runTaskFromQML(_taskId, _contractorID)
        {
            console.log("Running worker task from QML, ID = ", _taskId, " Contractor ID = ", _contractorID)
    
            Task.setTaskID(_taskId)
            Task.setContractorID(_contractorID)
            TaskController.startTask(Task)
        }
    }
    
    // .pro
    
    QT += quick
    CONFIG += c++11
    
    DEFINES += QT_DEPRECATED_WARNINGS
    
    SOURCES += \
            Contractor.cpp \
            main.cpp
    
    RESOURCES += qml.qrc
    
    # Default rules for deployment.
    qnx: target.path = /tmp/$${TARGET}/bin
    else: unix:!android: target.path = /opt/$${TARGET}/bin
    !isEmpty(target.path): INSTALLS += target
    
    HEADERS += \
        Contractor.h \
        ContractorTask.h \
        Controller.h
    


  • OK as @J.Hilk said:

    @J.Hilk said in Passing custom type pointers between threads via signals and slots cause app to crash:

    are you by any chance exposing those thread-shared custom types directly to qml?

    And:

    @J.Hilk said in Passing custom type pointers between QML and threads via signals and slots cause app to crash:

    I can only tell you that I always ran into trouble when I tried to access/manipulate c++ threaded stuff (directly)via QML.

    Indeed that was causing the crashes. The solution is to create a copy of the resource that is sent from QML and then send a copy of that resource to thread.

    As @J.Hilk suggested Manager object should do the job which is:

    @Matthew11 said in [Passing custom type pointers between QML and threads via signals and slots cause app to crash

    1. Manager <-> Threads :
    • communicate via signal/slots with QueuedConnection
    • synchronization/locking on the shared resource
    1. Manager <-> QML
    • signals and slots
    • or directly from the manager's memory

    Below you can find my working example. This is very similar to the code which I provided in the first post. You send a dispatch from QML to specific Contractor, Contractor then is doing his job and return the result back to QML (sends task with input data scenario). Or you send a dispatch to Contractor to retrieve some data (send task with no input data scenario). ContractorTask is no longer exposed to QML. But pointers are no longer send however it is possible in the C++ domain (across main (Manager) and workers threads with proper locking/synchronization).

    If you want to feel how it is when app is crashing uncomment the line _taskCopy.setData(_data); from pushTaskToContractor() in Controller.h which disabling the step of making the copy of the resource.

    Thank you all for your help in solving the problem!

    Code:

    //.pro
    
    QT += quick
    CONFIG += c++11
    
    SOURCES += \
            Contractor.cpp \
            main.cpp
    
    RESOURCES += qml.qrc
    
    HEADERS += \
        Contractor.h \
        ContractorTask.h \
        Controller.h
    
    // Contractor.h
    
    #ifndef CONTRACTOR_H
    #define CONTRACTOR_H
    
    #include <QObject>
    
    #include <ContractorTask.h>
    
    class Contractor : public QObject
    {
        Q_OBJECT
    
    public:
        Contractor(int _ID, QObject* parent = nullptr);
    
        int getID() { return ID; }
    
    public slots:
        void executeTask(ContractorTask _task);
    
    signals:
        void finished();
        void taskStarted(ContractorTask _task);
        void taskFinished(ContractorTask _task);
    
    private:
        int ID;
    };
    
    #endif // CONTRACTOR_H
    
    // Contractor.cpp
    
    #include "Contractor.h"
    
    #include <QDebug>
    #include <QThread>
    
    Contractor::Contractor(int _ID, QObject *parent) :
        QObject(parent),
        ID(_ID)
    {}
    
    void Contractor::executeTask(ContractorTask _task)
    {
        emit(taskStarted(_task));
    
        if(getID() != _task.getConctractorID())
        {
            qDebug() << "Not mine ID, discarding";
            return;
        }
    
        QVariant localData = _task.getData();
    
        switch(_task.getTaskID())
        {
        case 0: // PASS TASK TO C++ TO RETRIEVE DATA
        {
            QList<QVariant> _params;
            _params.append(12.5F);
            _params.append(14.36F);
    
            QVariant _data = _params;
    
            _task.setData(_data);
    
            qDebug() << "PASS TASK TO C++ TO RETRIEVE DATA";
        }
            break;
        case 1: // PASS TASK WITH DATA TO C++ AND GET THE SAME DATA BACK IN QML
        {
            QList<QVariant> _params;
            _params = localData.value<QList<QVariant>>();
    
            QList<float> _floats;
            int counter = 0;
            for(auto item : _params)
            {
                _floats << item.toFloat();
                qDebug() << "Getting data in C++ from QML (QList<float>): item =" << counter++ << "value =" << item;
            }
    
            qDebug() << "PASS TASK WITH DATA TO C++ AND GET THE SAME DATA BACK IN QML";
        }
            break;
        default:
        {
            qDebug() << "Oh... I don't have these one :(";
        }
        }
    
        emit(taskFinished(_task));
    }
    
    // ContractorTask.h
    
    #ifndef CONTRACTORTASK_H
    #define CONTRACTORTASK_H
    
    #include <QVariant>
    
    class ContractorTask
    {
    public:
        ContractorTask() :
            taskID(-1),
            contractorID(-1),
            data("")
        {}
    
        int getTaskID() { return taskID; }
    
        void setTaskID(int _ID) {taskID = _ID; }
    
        int getConctractorID() { return contractorID; }
    
        void setContractorID(int _ID) { contractorID = _ID; }
    
        QVariant getData() { return data; }
    
        void setData(QVariant _data) { data = _data; }
    
    private:
        int taskID;
        int contractorID;
        QVariant data;
    };
    
    Q_DECLARE_METATYPE(ContractorTask)
    
    #endif // CONTRACTORTASK_H
    
    
    // Controller.h
    
    #ifndef CONTROLLER
    #define CONTROLLER
    
    #include <QObject>
    #include <QThread>
    #include <QDebug>
    
    #include <Contractor.h>
    #include <ContractorTask.h>
    
    class Controller : public QObject
    {
        Q_OBJECT
        QThread workerThread;
    public:
        Controller()
        {
            Contractor *worker = new Contractor(0);
            worker->moveToThread(&workerThread);
    
            connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
    
            connect(this, &Controller::startTask, worker, &Contractor::executeTask);
            connect(worker, &Contractor::taskStarted, this, &Controller::contractorStartedTask);
            connect(worker, &Contractor::taskFinished, this, &Controller::contractorFinishedTask);
            workerThread.start();
        }
    
        ~Controller()
        {
            workerThread.quit();
            workerThread.wait();
        }
    
    signals:
        void startTask(ContractorTask);
        void taskStarted(int id, int contractor, QVariant data);
        void taskEnded(int id, int contractor, QVariant data);
    
    public slots:
        void pushTaskToContractor(int _id, int _contractor, QVariant _data)
        {
            // QVariant depends to QML, so make COPY of QVariant CONTENT, before passing it to thread:
            QList<QVariant> _params;
            _params = _data.value<QList<QVariant>>();
            QVariant _dataToSend = _params;
    
            ContractorTask _taskCopy;
            _taskCopy.setTaskID(_id);
            _taskCopy.setContractorID(_contractor);
            _taskCopy.setData(_dataToSend);                 // Sending local data copy is OK
    
    //        _taskCopy.setData(_data);                     // Sending _data (has source in QML) = PROGRAM CRASH!!!
    
            emit(startTask(_taskCopy));
        }
    
        void contractorFinishedTask(ContractorTask _task)
        {
            // Passing COPY of ContractorTask to QML:
            emit(taskEnded(_task.getTaskID(), _task.getConctractorID(), _task.getData()));
        }
    
        void contractorStartedTask(ContractorTask _task)
        {
            // Passing COPY of ContractorTask to QML:
            emit(taskStarted(_task.getTaskID(), _task.getConctractorID(), _task.getData()));
        }
    };
    
    #endif // CONTROLLER
    
    // main.cpp
    
    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <Controller.h>
    #include <QQmlContext>
    
    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    
        QGuiApplication app(argc, argv);
    
        Controller TaskController;
        qRegisterMetaType<ContractorTask>();
    
        QQmlApplicationEngine engine;
        engine.rootContext()->setContextProperty("TaskController", &TaskController);
    
        const QUrl url(QStringLiteral("qrc:/main.qml"));
        QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                         &app, [url](QObject *obj, const QUrl &objUrl) {
            if (!obj && url == objUrl)
                QCoreApplication::exit(-1);
        }, Qt::QueuedConnection);
        engine.load(url);
    
    
        // Get item from QML, and connect it's signal (startTaskFromQML) to Controller
        QObject *item = engine.rootObjects().first();
        QObject::connect(item, SIGNAL(startTaskFromQML(int, int, QVariant)),
                         &TaskController, SLOT(pushTaskToContractor(int, int, QVariant)));
    
    
        return app.exec();
    }
    
    // main.qml
    
    import QtQuick 2.12
    import QtQuick.Controls 2.5
    
    ApplicationWindow {
        id: root
        visible: true
        width: 640
        height: 480
        title: qsTr("Test")
    
        signal startTaskFromQML(int id, int contractor, variant data)
        property variant _data: 0
    
        Connections {
            target: TaskController
    
            onTaskEnded: console.log("Contractor with ID =", contractor, "finished task with ID = ", id, "and returned result:", data);
    
            onTaskStarted: console.log("Contractor with ID =", contractor, "started task with ID = ", id);
        }
    
        Column {
            id: column
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.verticalCenter: parent.verticalCenter
            Button {
                id: passAndGet
                text: qsTr("PASS TASK WITH DATA TO C++ AND GET THE SAME DATA BACK IN QML")
                anchors.horizontalCenter: parent.horizontalCenter
    
                onClicked: {
                    _data= [1.2, 3.4, 5.6, 7.8]
    
                    for(var i = 0; i < 50; i++)
                    {
                        root.startTaskFromQML(1, 0, _data)
                    }
                }
            }
    
            Button {
                id: getData
                text: qsTr("PASS TASK TO C++ TO RETRIEVE DATA")
                anchors.horizontalCenter: parent.horizontalCenter
    
                onClicked: {
                    _data = 0
                    root.startTaskFromQML(0, 0, _data)
                }
            }
        }
    }
    
    // qtquickcontrols2.conf
    
    [Controls]
    Style=Material
    
    // qml.qrc
    
    <RCC>
        <qresource prefix="/">
            <file>main.qml</file>
            <file>qtquickcontrols2.conf</file>
        </qresource>
    </RCC>
    

  • Lifetime Qt Champion

    Hi,

    What is the exact goal for that QVariant ?


  • Qt Champions 2017

    @Matthew11 said in Passing custom type pointers between threads via signals and slots cause app to crash:

    I'm guessing that crash is caused by accessing the shared resource (my custom class instance). I tried to add some serialization using QMutex, with and without QMutexLocker, QReadWriteLocker but the result was the same - crash.

    Crashes have stack traces. Please provide one. Also as a hint:

    Q_INVOKABLE void setData(QVariant _data)
    {
        Q_ASSERT(QThread::currentThread() == thread());
    
        qDebug() << "setData()" << QThread::currentThreadId();
        data = _data;
    }
    


  • @SGaist said in Passing custom type pointers between threads via signals and slots cause app to crash:

    What is the exact goal for that QVariant ?

    Well, simply speaking, to pass various data between QML and C++ (floats, integers, QStrings and any that will be needed) within the ContractorTask object.

    Use of case: The UI send request (with taskID, contractorID (both ints) and data (QVariant) as ContractorTask object) then the specific Contractor (another thread) run specific code and return the result (QVariant) which is also included in ContractorTask object. Then in QML, I know which task was started and ended and with what data -> the UI can be updated. As well in the opposite direction (C++ received event, then sends data to QML). I also have interfaces (not included in the sources that I provided) for Contractor and Controller which relay on ContractorTask object and simplifies things with making new Contractors passing them to Controller which takes care of connections, clean up (things that Controller does).



  • @kshegunov said in Passing custom type pointers between threads via signals and slots cause app to crash:

    Crashes have stack traces. Please provide one. Also as a hint:
    Q_ASSERT(QThread::currentThread() == thread());

    So, If I understand it correctly, it gives me insights where and when another thread is working on resources from another thread? So it happens when Contractor is working on _task in Contractor::executeTask(ContractorTask*) which is "normal" in this case. And these places should be protected. Am I right, that's the hint?

    Returning to stack traces, I generated them for the same code included before. App crashes seem to be connected with QV4Object. 3'rd and 6'th seem to be more common. Is the table content enough or will it be better with screenshots?

    -->1<--
    
    1   QV4::Object::get                           qv4object_p.h                308  0x7ffff714c2fc 
    2   QV4::Runtime::method_callProperty          qv4runtime.cpp               1376 0x7ffff714c2fc 
    3   QV4::Moth::VME::interpret                  qv4vme_moth.cpp              718  0x7ffff70e3f90 
    4   QV4::Moth::VME::exec                       qv4vme_moth.cpp              441  0x7ffff70e7cc4 
    5   QV4::ArrowFunction::virtualCall            qv4functionobject.cpp        513  0x7ffff7080720 
    6   QV4::FunctionObject::call                  qv4functionobject_p.h        202  0x7ffff71474a3 
    7   QV4::Runtime::method_callName              qv4runtime.cpp               1346 0x7ffff71474a3 
    8   QV4::Moth::VME::interpret                  qv4vme_moth.cpp              745  0x7ffff70e3e37 
    9   QV4::Moth::VME::exec                       qv4vme_moth.cpp              441  0x7ffff70e7cc4 
    10  QV4::Function::call                        qv4function.cpp              68   0x7ffff707fade 
    11  QQmlJavaScriptExpression::evaluate         qqmljavascriptexpression.cpp 216  0x7ffff71e7eb8 
    12  QQmlBoundSignalExpression::evaluate        qqmlboundsignal.cpp          225  0x7ffff718aa52 
    13  QQmlBoundSignal_callback                   qqmlboundsignal.cpp          358  0x7ffff718bf53 
    14  QQmlNotifier::emitNotify                   qqmlnotifier.cpp             106  0x7ffff71c9cf4 
    15  QQmlData::signalEmitted                    qqmlengine.cpp               880  0x7ffff716e274 
    16  QMetaObject::activate                      qobject.cpp                  3648 0x7ffff6a20c92 
    17  QQuickAbstractButtonPrivate::handleRelease qquickabstractbutton.cpp     179  0x7fffd0d71ca8 
    18  QQuickControl::mouseReleaseEvent           qquickcontrol.cpp            2099 0x7fffd0d8c9f5 
    19  QQuickItem::event                          qquickitem.cpp               8096 0x7fffd22c98de 
    20  QCoreApplication::notifyInternal2          qcoreapplication.cpp         1061 0x7ffff69f4c18         
    
    --> 2 <--
    
    1   QV4::IdentifierHash::lookup         qv4identifier.cpp            173  0x7ffff7058c28 
    2   QV4::IdentifierHash::value          qv4identifier_p.h            165  0x7ffff70a6bc5 
    3   QV4::QQmlContextWrapper::virtualGet qv4qmlcontext.cpp            196  0x7ffff70a6bc5 
    4   QV4::Object::get                    qv4object_p.h                314  0x7ffff7055b65 
    5   QV4::ExecutionContext::getProperty  qv4context.cpp               360  0x7ffff7055b65 
    6   QV4::Runtime::method_loadName       qv4runtime.cpp               985  0x7ffff7143df0 
    7   QV4::Moth::VME::interpret           qv4vme_moth.cpp              548  0x7ffff70e413e 
    8   QV4::Moth::VME::exec                qv4vme_moth.cpp              441  0x7ffff70e7cc4 
    9   QV4::ArrowFunction::virtualCall     qv4functionobject.cpp        513  0x7ffff7080720 
    10  QV4::FunctionObject::call           qv4functionobject_p.h        202  0x7ffff71474a3 
    11  QV4::Runtime::method_callName       qv4runtime.cpp               1346 0x7ffff71474a3 
    12  QV4::Moth::VME::interpret           qv4vme_moth.cpp              745  0x7ffff70e3e37 
    13  QV4::Moth::VME::exec                qv4vme_moth.cpp              441  0x7ffff70e7cc4 
    14  QV4::Function::call                 qv4function.cpp              68   0x7ffff707fade 
    15  QQmlJavaScriptExpression::evaluate  qqmljavascriptexpression.cpp 216  0x7ffff71e7eb8 
    16  QQmlBoundSignalExpression::evaluate qqmlboundsignal.cpp          225  0x7ffff718aa52 
    17  QQmlBoundSignal_callback            qqmlboundsignal.cpp          358  0x7ffff718bf53 
    18  QQmlNotifier::emitNotify            qqmlnotifier.cpp             106  0x7ffff71c9cf4 
    19  QQmlData::signalEmitted             qqmlengine.cpp               880  0x7ffff716e274 
    20  QMetaObject::activate               qobject.cpp                  3648 0x7ffff6a20c92 
    
    --> 3 <--
    
    1  QV4::Heap::String::append                        qv4string.cpp                235  0x7ffff714dcdc 
    2  QV4::Heap::String::simplifyString                qv4string.cpp                184  0x7ffff714de22 
    3  QV4::String::createPropertyKeyImpl               qv4string.cpp                172  0x7ffff714dfcb 
    4  QV4::StringOrSymbol::createPropertyKey           qv4string_p.h                311  0x7ffff714c324 
    5  QV4::StringOrSymbol::toPropertyKey               qv4string_p.h                316  0x7ffff714c324 
    6  QV4::Object::get                                 qv4object_p.h                308  0x7ffff714c324 
    7  QV4::Runtime::method_callProperty                qv4runtime.cpp               1376 0x7ffff714c324 
    8  QV4::Moth::VME::interpret                        qv4vme_moth.cpp              718  0x7ffff70e3f90 
    9  QV4::Moth::VME::exec                             qv4vme_moth.cpp              441  0x7ffff70e7cc4 
    10 QV4::ArrowFunction::virtualCall                  qv4functionobject.cpp        513  0x7ffff7080720 
    11 QV4::FunctionObject::call                        qv4functionobject_p.h        202  0x7ffff71474a3 
    12 QV4::Runtime::method_callName                    qv4runtime.cpp               1346 0x7ffff71474a3 
    13 QV4::Moth::VME::interpret                        qv4vme_moth.cpp              745  0x7ffff70e3e37 
    14 QV4::Moth::VME::exec                             qv4vme_moth.cpp              441  0x7ffff70e7cc4 
    15 QV4::Function::call                              qv4function.cpp              68   0x7ffff707fade 
    16 QQmlJavaScriptExpression::evaluate               qqmljavascriptexpression.cpp 216  0x7ffff71e7eb8 
    17 QQmlBoundSignalExpression::evaluate              qqmlboundsignal.cpp          225  0x7ffff718aa52 
    18 QQmlBoundSignal_callback                         qqmlboundsignal.cpp          358  0x7ffff718bf53 
    19 QQmlNotifier::emitNotify                         qqmlnotifier.cpp             106  0x7ffff71c9cf4 
    20 QQmlData::signalEmitted                          qqmlengine.cpp               880  0x7ffff716e274 
    21 QMetaObject::activate                            qobject.cpp                  3648 0x7ffff6a20c92 
    22 QQuickAbstractButtonPrivate::handleRelease       qquickabstractbutton.cpp     179  0x7fffd0d71ca8 
    23 QQuickControl::mouseReleaseEvent                 qquickcontrol.cpp            2099 0x7fffd0d8c9f5 
    24 QQuickItem::event                                qquickitem.cpp               8096 0x7fffd22c98de 
    25 QCoreApplication::notifyInternal2                qcoreapplication.cpp         1061 0x7ffff69f4c18 
    26 QCoreApplication::sendEvent                      qcoreapplication.cpp         1451 0x7ffff69f4dce 
    27 QQuickWindowPrivate::deliverMouseEvent           qquickwindow.cpp             1784 0x7fffd22e23bf 
    28 QQuickWindowPrivate::deliverPointerEvent         qquickwindow.cpp             2346 0x7fffd22e36fb 
    29 QQuickWindowPrivate::handleMouseEvent            qquickwindow.cpp             2210 0x7fffd22e4535 
    30 QWindow::event                                   qwindow.cpp                  2336 0x7ffff77044eb 
    31 QQuickWindow::event                              qquickwindow.cpp             1673 0x7fffd22e54e5 
    32 QCoreApplication::notifyInternal2                qcoreapplication.cpp         1061 0x7ffff69f4c18 
    33 QCoreApplication::sendSpontaneousEvent           qcoreapplication.cpp         1463 0x7ffff69f4dde 
    34 QGuiApplicationPrivate::processMouseEvent        qguiapplication.cpp          2102 0x7ffff76f91e7 
    35 QGuiApplicationPrivate::processWindowSystemEvent qguiapplication.cpp          1837 0x7ffff76fa795 
    36 QWindowSystemInterface::sendWindowSystemEvents   qwindowsysteminterface.cpp   1068 0x7ffff76d644b 
    37 xcbSourceDispatch                                qxcbeventdispatcher.cpp      105  0x7ffff014767a 
    38 g_main_context_dispatch                                                            0x7ffff24dc417 
    39 ??                                                                                 0x7ffff24dc650 
    40 g_main_context_iteration                                                           0x7ffff24dc6dc 
    41 QEventDispatcherGlib::processEvents              qeventdispatcher_glib.cpp    422  0x7ffff6a4bdcf 
    42 QEventLoop::exec                                 qeventloop.cpp               225  0x7ffff69f357a 
    43 QCoreApplication::exec                           qcoreapplication.cpp         1364 0x7ffff69fbf80 
    44 main                                             main.cpp                     28   0x55555555be66 
    
    --> 4 <--
    
    
    1  QV4::Object::get                                                                                                                          qv4object_p.h             310  0x7ffff713cee4 
    2  objectToVariant                                                                                                                           qv4engine.cpp             1389 0x7ffff713cee4 
    3  toVariant                                                                                                                                 qv4engine.cpp             1359 0x7ffff713d949 
    4  QV4::ExecutionEngine::toVariant                                                                                                           qv4engine.cpp             1252 0x7ffff713dc10 
    5  QJSValue::toVariant                                                                                                                       qjsvalue.cpp              714  0x7ffff70521e4 
    6  convertJSValueToVariantType<QList<QVariant>>                                                                                              qv8engine.cpp             100  0x7ffff721b4c1 
    7  QtPrivate::ConverterFunctor<QJSValue, QList<QVariant>, QList<QVariant> ( *)(QJSValue const&)>::convert                                    qmetatype.h               398  0x7ffff721b162 
    8  QMetaType::convert                                                                                                                        qmetatype.cpp             739  0x7ffff6a0b36d 
    9  (anonymous namespace)::convert                                                                                                            qvariant.cpp              393  0x7ffff6a3ada1 
    10 QtPrivate::QVariantValueHelper<QList<QVariant>>::metaType                                                                                 qvariant.h                725  0x55555555a42e 
    11 QtPrivate::MetaTypeInvoker<QtPrivate::QVariantValueHelper<QList<QVariant>>, QVariant const&, QList<QVariant>>::invoke                     qvariant.h                115  0x5555555594c7 
    12 QtPrivate::QVariantValueHelperInterface<QList<QVariant>>::invoke                                                                          qvariant.h                793  0x5555555589f0 
    13 qvariant_cast<QList<QVariant>>                                                                                                            qvariant.h                860  0x55555555a536 
    14 QVariant::value<QList<QVariant>>                                                                                                          qvariant.h                362  0x555555559576 
    15 Contractor::executeTask                                                                                                                   Contractor.cpp            44   0x555555557a9a 
    16 QtPrivate::FunctorCall<QtPrivate::IndexesList<0>, QtPrivate::List<ContractorTask *>, void, void (Contractor:: *)(ContractorTask *)>::call qobjectdefs_impl.h        152  0x55555555e49b 
    17 QtPrivate::FunctionPointer<void (Contractor:: *)(ContractorTask *)>::call<QtPrivate::List<ContractorTask *>, void>                        qobjectdefs_impl.h        185  0x55555555e179 
    18 QtPrivate::QSlotObject<void (Contractor:: *)(ContractorTask *), QtPrivate::List<ContractorTask *>, void>::impl                            qobjectdefs_impl.h        414  0x55555555de01 
    19 QObject::event                                                                                                                            qobject.cpp               1249 0x7ffff6a21581 
    20 QCoreApplication::notifyInternal2                                                                                                         qcoreapplication.cpp      1061 0x7ffff69f4c18 
    21 QCoreApplication::sendEvent                                                                                                               qcoreapplication.cpp      1451 0x7ffff69f4dce 
    22 QCoreApplicationPrivate::sendPostedEvents                                                                                                 qcoreapplication.cpp      1800 0x7ffff69f7647 
    23 QCoreApplication::sendPostedEvents                                                                                                        qcoreapplication.cpp      1654 0x7ffff69f7b28 
    24 postEventSourceDispatch                                                                                                                   qeventdispatcher_glib.cpp 276  0x7ffff6a4c793 
    25 g_main_context_dispatch                                                                                                                                                  0x7ffff24dc417 
    26 ??                                                                                                                                                                       0x7ffff24dc650 
    27 g_main_context_iteration                                                                                                                                                 0x7ffff24dc6dc 
    28 QEventDispatcherGlib::processEvents                                                                                                       qeventdispatcher_glib.cpp 422  0x7ffff6a4bdcf 
    29 QEventLoop::exec                                                                                                                          qeventloop.cpp            225  0x7ffff69f357a 
    30 QThread::exec                                                                                                                             qthread.cpp               531  0x7ffff682b7dc 
    31 QThreadPrivate::start                                                                                                                     qthread_unix.cpp          361  0x7ffff682cd13 
    32 start_thread                                                                                                                              pthread_create.c          463  0x7ffff5bdd6db 
    33 clone                                                                                                                                     clone.S                   95   0x7ffff5f1688f                                            
    
    --> 5 <--
    
    1   QV4::Object::ownPropertyKeys                                                                                                              qv4object_p.h         367  0x7ffff713cbdd 
    2   QV4::ObjectIterator::ObjectIterator                                                                                                       qv4objectiterator_p.h 80   0x7ffff713cbdd 
    3   objectToVariant                                                                                                                           qv4engine.cpp         1397 0x7ffff713cbdd 
    4   toVariant                                                                                                                                 qv4engine.cpp         1359 0x7ffff713d949 
    5   QV4::ExecutionEngine::toVariant                                                                                                           qv4engine.cpp         1252 0x7ffff713dc10 
    6   QJSValue::toVariant                                                                                                                       qjsvalue.cpp          714  0x7ffff70521e4 
    7   convertJSValueToVariantType<QList<QVariant>>                                                                                              qv8engine.cpp         100  0x7ffff721b4c1 
    8   QtPrivate::ConverterFunctor<QJSValue, QList<QVariant>, QList<QVariant> ( *)(QJSValue const&)>::convert                                    qmetatype.h           398  0x7ffff721b162 
    9   QMetaType::convert                                                                                                                        qmetatype.cpp         739  0x7ffff6a0b36d 
    10  (anonymous namespace)::convert                                                                                                            qvariant.cpp          393  0x7ffff6a3ada1 
    11  QtPrivate::QVariantValueHelper<QList<QVariant>>::metaType                                                                                 qvariant.h            725  0x55555555a42e 
    12  QtPrivate::MetaTypeInvoker<QtPrivate::QVariantValueHelper<QList<QVariant>>, QVariant const&, QList<QVariant>>::invoke                     qvariant.h            115  0x5555555594c7 
    13  QtPrivate::QVariantValueHelperInterface<QList<QVariant>>::invoke                                                                          qvariant.h            793  0x5555555589f0 
    14  qvariant_cast<QList<QVariant>>                                                                                                            qvariant.h            860  0x55555555a536 
    15  QVariant::value<QList<QVariant>>                                                                                                          qvariant.h            362  0x555555559576 
    16  Contractor::executeTask                                                                                                                   Contractor.cpp        44   0x555555557a9a 
    17  QtPrivate::FunctorCall<QtPrivate::IndexesList<0>, QtPrivate::List<ContractorTask *>, void, void (Contractor:: *)(ContractorTask *)>::call qobjectdefs_impl.h    152  0x55555555e49b 
    18  QtPrivate::FunctionPointer<void (Contractor:: *)(ContractorTask *)>::call<QtPrivate::List<ContractorTask *>, void>                        qobjectdefs_impl.h    185  0x55555555e179 
    19  QtPrivate::QSlotObject<void (Contractor:: *)(ContractorTask *), QtPrivate::List<ContractorTask *>, void>::impl                            qobjectdefs_impl.h    414  0x55555555de01 
    20  QObject::event                                                                                                                            qobject.cpp           1249 0x7ffff6a21581 
    ... <More>                                                                                                                                                                              
    
    --> 6 <--
    
    1   QV4::Heap::String::append                  qv4string.cpp         235  0x7ffff714dcdc 
    2   QV4::Heap::String::simplifyString          qv4string.cpp         184  0x7ffff714de22 
    3   QV4::Heap::StringOrSymbol::createHashValue qv4string.cpp         245  0x7ffff714e185 
    4   QV4::Heap::StringOrSymbol::hashValue       qv4string_p.h         97   0x7ffff70ceb35 
    5   QV4::Heap::String::isEqualTo               qv4string_p.h         128  0x7ffff70ceb35 
    6   QV4::String::equals                        qv4string_p.h         203  0x7ffff70cb3db 
    7   QV4::QObjectWrapper::getQmlProperty        qv4qobjectwrapper.cpp 283  0x7ffff70cb3db 
    8   QV4::QObjectWrapper::getQmlProperty        qv4qobjectwrapper.cpp 414  0x7ffff70cbc4e 
    9   QV4::QQmlContextWrapper::virtualGet        qv4qmlcontext.cpp     246  0x7ffff70a6c66 
    10  QV4::Object::get                           qv4object_p.h         314  0x7ffff7055b65 
    11  QV4::ExecutionContext::getProperty         qv4context.cpp        360  0x7ffff7055b65 
    12  QV4::Runtime::method_loadName              qv4runtime.cpp        985  0x7ffff7143df0 
    13  QV4::Moth::VME::interpret                  qv4vme_moth.cpp       548  0x7ffff70e413e 
    14  QV4::Moth::VME::exec                       qv4vme_moth.cpp       441  0x7ffff70e7cc4 
    15  QV4::ArrowFunction::virtualCall            qv4functionobject.cpp 513  0x7ffff7080720 
    16  QV4::FunctionObject::call                  qv4functionobject_p.h 202  0x7ffff71474a3 
    17  QV4::Runtime::method_callName              qv4runtime.cpp        1346 0x7ffff71474a3 
    18  QV4::Moth::VME::interpret                  qv4vme_moth.cpp       745  0x7ffff70e3e37 
    19  QV4::Moth::VME::exec                       qv4vme_moth.cpp       441  0x7ffff70e7cc4 
    20  QV4::Function::call                        qv4function.cpp       68   0x7ffff707fade 
    ... <More>                                                                               
    
    

  • Qt Champions 2017

    @Matthew11 said in Passing custom type pointers between threads via signals and slots cause app to crash:

    So, If I understand it correctly, it gives me insights where and when another thread is working on resources from another thread?

    Indeed.

    So it happens when Contractor is working on _task in Contractor::executeTask(ContractorTask*) which is "normal" in this case. And these places should be protected. Am I right, that's the hint?

    Yes, although I'm not quite convinced you're going to manage that really gracefully. Why isn't the task something which belongs to a specific thread (i.e. moved to and operated on) by a single thread?

    Returning to stack traces, I generated them for the same code included before. App crashes seem to be connected with QV4Object. 3'rd and 6'th seem to be more common.

    Look down. These come from QML, there're calls into your objects that originate from different threads. I imagine that's because the Task has no notion of ownership. I'm not much into QML but it seems you get your code called from different threads, which is a bad idea.

    Is the table content enough or will it be better with screenshots?

    It's fine.



  • @kshegunov said in Passing custom type pointers between threads via signals and slots cause app to crash:

    Yes, although I'm not quite convinced you're going to manage that really gracefully.

    This is why when I adopted mutexes in Contractor::executeTask(ContractorTask*) or inside ContractorTask it's still crashing? Or maybe it wasn't done well by me?

    @kshegunov said in Passing custom type pointers between threads via signals and slots cause app to crash:

    Why isn't the task something which belongs to a specific thread (i.e. moved to and operated on) by a single thread?

    So, when ContractorTask is shared (and called from different threads), it dramatically cut down the number of connections via threads and QML but also makes it vulnerable. And by using QVariant I can put there various data types from standard types to custom types, which I need in order to present user or send user input to the worker (and then further) which also cut down the number of connect's. And yeah, (correct me if I am wrong) I can do this with countable numbers of signals/slots passing different parameters with QueuedConnection and passing the data by value which is a (AFAIK) safe solution. But firstly I would like to give a try with using the sharaed resource and make it safe.


  • Moderators

    @Matthew11
    are you by any chance exposing those thread-shared custom types directly to qml?

    If yes, then that is a bad idea and my very well be the reason for your issues.



  • @J.Hilk said in Passing custom type pointers between threads via signals and slots cause app to crash:

    @Matthew11 are you by any chance exposing those thread-shared custom types directly to qml?

    If yes, then that is a bad idea and my very well be the reason for your issues.

    Unfortunately yes, and it actually happening:

    // main.cpp:
    engine.rootContext()->setContextProperty("Task", &Task);
    

    And in QML I am working directly on that shared resources:

    // main.qml:
    Task.setTaskID(_taskId)
    Task.setContractorID(
    TaskController.startTask(Task)
    

    OK, that's the good clue. Could you tell more why is that? Is it connected with JS / qml engine stuff?


  • Moderators

    @Matthew11
    you should probably wrap that in manager class instead.

    Could you tell more why is that? Is it connected with JS / qml engine stuff

    most likely. I don't know enough about the inner workings to give you a definite answer. I can only tell you that I always ran into trouble when I tried to access/manipulate c++ threaded stuff (directly)via QML.

    Since than I always have a manger object that forwards stuff via signals or from its own memory when exposed directly.



  • @J.Hilk said in Passing custom type pointers between threads via signals and slots cause app to crash:

    you should probably wrap that in manager class instead.
    Since than I always have a manger object that forwards stuff via signals or from its own memory when exposed directly.

    I was going to do it like this.

    Let's clarify some aspects of your approach.

    1. Manager <-> Threads :
    • communicate via signal/slots with QueuedConnection
    • synchronization/locking on the shared resource

    @J.Hilk said in Passing custom type pointers between threads via signals and slots cause app to crash:

    manger object that forwards stuff via signals or from its own memory when exposed directly.

    1. Manager <-> QML
    • signals and slots
    • or directly from manager's memory

    Have I figured it out properly?


  • Moderators

    @Matthew11
    jep, pretty much.
    That at least is how I (would)do it.



  • OK as @J.Hilk said:

    @J.Hilk said in Passing custom type pointers between threads via signals and slots cause app to crash:

    are you by any chance exposing those thread-shared custom types directly to qml?

    And:

    @J.Hilk said in Passing custom type pointers between QML and threads via signals and slots cause app to crash:

    I can only tell you that I always ran into trouble when I tried to access/manipulate c++ threaded stuff (directly)via QML.

    Indeed that was causing the crashes. The solution is to create a copy of the resource that is sent from QML and then send a copy of that resource to thread.

    As @J.Hilk suggested Manager object should do the job which is:

    @Matthew11 said in [Passing custom type pointers between QML and threads via signals and slots cause app to crash

    1. Manager <-> Threads :
    • communicate via signal/slots with QueuedConnection
    • synchronization/locking on the shared resource
    1. Manager <-> QML
    • signals and slots
    • or directly from the manager's memory

    Below you can find my working example. This is very similar to the code which I provided in the first post. You send a dispatch from QML to specific Contractor, Contractor then is doing his job and return the result back to QML (sends task with input data scenario). Or you send a dispatch to Contractor to retrieve some data (send task with no input data scenario). ContractorTask is no longer exposed to QML. But pointers are no longer send however it is possible in the C++ domain (across main (Manager) and workers threads with proper locking/synchronization).

    If you want to feel how it is when app is crashing uncomment the line _taskCopy.setData(_data); from pushTaskToContractor() in Controller.h which disabling the step of making the copy of the resource.

    Thank you all for your help in solving the problem!

    Code:

    //.pro
    
    QT += quick
    CONFIG += c++11
    
    SOURCES += \
            Contractor.cpp \
            main.cpp
    
    RESOURCES += qml.qrc
    
    HEADERS += \
        Contractor.h \
        ContractorTask.h \
        Controller.h
    
    // Contractor.h
    
    #ifndef CONTRACTOR_H
    #define CONTRACTOR_H
    
    #include <QObject>
    
    #include <ContractorTask.h>
    
    class Contractor : public QObject
    {
        Q_OBJECT
    
    public:
        Contractor(int _ID, QObject* parent = nullptr);
    
        int getID() { return ID; }
    
    public slots:
        void executeTask(ContractorTask _task);
    
    signals:
        void finished();
        void taskStarted(ContractorTask _task);
        void taskFinished(ContractorTask _task);
    
    private:
        int ID;
    };
    
    #endif // CONTRACTOR_H
    
    // Contractor.cpp
    
    #include "Contractor.h"
    
    #include <QDebug>
    #include <QThread>
    
    Contractor::Contractor(int _ID, QObject *parent) :
        QObject(parent),
        ID(_ID)
    {}
    
    void Contractor::executeTask(ContractorTask _task)
    {
        emit(taskStarted(_task));
    
        if(getID() != _task.getConctractorID())
        {
            qDebug() << "Not mine ID, discarding";
            return;
        }
    
        QVariant localData = _task.getData();
    
        switch(_task.getTaskID())
        {
        case 0: // PASS TASK TO C++ TO RETRIEVE DATA
        {
            QList<QVariant> _params;
            _params.append(12.5F);
            _params.append(14.36F);
    
            QVariant _data = _params;
    
            _task.setData(_data);
    
            qDebug() << "PASS TASK TO C++ TO RETRIEVE DATA";
        }
            break;
        case 1: // PASS TASK WITH DATA TO C++ AND GET THE SAME DATA BACK IN QML
        {
            QList<QVariant> _params;
            _params = localData.value<QList<QVariant>>();
    
            QList<float> _floats;
            int counter = 0;
            for(auto item : _params)
            {
                _floats << item.toFloat();
                qDebug() << "Getting data in C++ from QML (QList<float>): item =" << counter++ << "value =" << item;
            }
    
            qDebug() << "PASS TASK WITH DATA TO C++ AND GET THE SAME DATA BACK IN QML";
        }
            break;
        default:
        {
            qDebug() << "Oh... I don't have these one :(";
        }
        }
    
        emit(taskFinished(_task));
    }
    
    // ContractorTask.h
    
    #ifndef CONTRACTORTASK_H
    #define CONTRACTORTASK_H
    
    #include <QVariant>
    
    class ContractorTask
    {
    public:
        ContractorTask() :
            taskID(-1),
            contractorID(-1),
            data("")
        {}
    
        int getTaskID() { return taskID; }
    
        void setTaskID(int _ID) {taskID = _ID; }
    
        int getConctractorID() { return contractorID; }
    
        void setContractorID(int _ID) { contractorID = _ID; }
    
        QVariant getData() { return data; }
    
        void setData(QVariant _data) { data = _data; }
    
    private:
        int taskID;
        int contractorID;
        QVariant data;
    };
    
    Q_DECLARE_METATYPE(ContractorTask)
    
    #endif // CONTRACTORTASK_H
    
    
    // Controller.h
    
    #ifndef CONTROLLER
    #define CONTROLLER
    
    #include <QObject>
    #include <QThread>
    #include <QDebug>
    
    #include <Contractor.h>
    #include <ContractorTask.h>
    
    class Controller : public QObject
    {
        Q_OBJECT
        QThread workerThread;
    public:
        Controller()
        {
            Contractor *worker = new Contractor(0);
            worker->moveToThread(&workerThread);
    
            connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
    
            connect(this, &Controller::startTask, worker, &Contractor::executeTask);
            connect(worker, &Contractor::taskStarted, this, &Controller::contractorStartedTask);
            connect(worker, &Contractor::taskFinished, this, &Controller::contractorFinishedTask);
            workerThread.start();
        }
    
        ~Controller()
        {
            workerThread.quit();
            workerThread.wait();
        }
    
    signals:
        void startTask(ContractorTask);
        void taskStarted(int id, int contractor, QVariant data);
        void taskEnded(int id, int contractor, QVariant data);
    
    public slots:
        void pushTaskToContractor(int _id, int _contractor, QVariant _data)
        {
            // QVariant depends to QML, so make COPY of QVariant CONTENT, before passing it to thread:
            QList<QVariant> _params;
            _params = _data.value<QList<QVariant>>();
            QVariant _dataToSend = _params;
    
            ContractorTask _taskCopy;
            _taskCopy.setTaskID(_id);
            _taskCopy.setContractorID(_contractor);
            _taskCopy.setData(_dataToSend);                 // Sending local data copy is OK
    
    //        _taskCopy.setData(_data);                     // Sending _data (has source in QML) = PROGRAM CRASH!!!
    
            emit(startTask(_taskCopy));
        }
    
        void contractorFinishedTask(ContractorTask _task)
        {
            // Passing COPY of ContractorTask to QML:
            emit(taskEnded(_task.getTaskID(), _task.getConctractorID(), _task.getData()));
        }
    
        void contractorStartedTask(ContractorTask _task)
        {
            // Passing COPY of ContractorTask to QML:
            emit(taskStarted(_task.getTaskID(), _task.getConctractorID(), _task.getData()));
        }
    };
    
    #endif // CONTROLLER
    
    // main.cpp
    
    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <Controller.h>
    #include <QQmlContext>
    
    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    
        QGuiApplication app(argc, argv);
    
        Controller TaskController;
        qRegisterMetaType<ContractorTask>();
    
        QQmlApplicationEngine engine;
        engine.rootContext()->setContextProperty("TaskController", &TaskController);
    
        const QUrl url(QStringLiteral("qrc:/main.qml"));
        QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                         &app, [url](QObject *obj, const QUrl &objUrl) {
            if (!obj && url == objUrl)
                QCoreApplication::exit(-1);
        }, Qt::QueuedConnection);
        engine.load(url);
    
    
        // Get item from QML, and connect it's signal (startTaskFromQML) to Controller
        QObject *item = engine.rootObjects().first();
        QObject::connect(item, SIGNAL(startTaskFromQML(int, int, QVariant)),
                         &TaskController, SLOT(pushTaskToContractor(int, int, QVariant)));
    
    
        return app.exec();
    }
    
    // main.qml
    
    import QtQuick 2.12
    import QtQuick.Controls 2.5
    
    ApplicationWindow {
        id: root
        visible: true
        width: 640
        height: 480
        title: qsTr("Test")
    
        signal startTaskFromQML(int id, int contractor, variant data)
        property variant _data: 0
    
        Connections {
            target: TaskController
    
            onTaskEnded: console.log("Contractor with ID =", contractor, "finished task with ID = ", id, "and returned result:", data);
    
            onTaskStarted: console.log("Contractor with ID =", contractor, "started task with ID = ", id);
        }
    
        Column {
            id: column
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.verticalCenter: parent.verticalCenter
            Button {
                id: passAndGet
                text: qsTr("PASS TASK WITH DATA TO C++ AND GET THE SAME DATA BACK IN QML")
                anchors.horizontalCenter: parent.horizontalCenter
    
                onClicked: {
                    _data= [1.2, 3.4, 5.6, 7.8]
    
                    for(var i = 0; i < 50; i++)
                    {
                        root.startTaskFromQML(1, 0, _data)
                    }
                }
            }
    
            Button {
                id: getData
                text: qsTr("PASS TASK TO C++ TO RETRIEVE DATA")
                anchors.horizontalCenter: parent.horizontalCenter
    
                onClicked: {
                    _data = 0
                    root.startTaskFromQML(0, 0, _data)
                }
            }
        }
    }
    
    // qtquickcontrols2.conf
    
    [Controls]
    Style=Material
    
    // qml.qrc
    
    <RCC>
        <qresource prefix="/">
            <file>main.qml</file>
            <file>qtquickcontrols2.conf</file>
        </qresource>
    </RCC>