Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QThread - moveToThread: Cannot move objects with a parent



  • I'm developing a main application that executes a set of plugins (shared library) in a serial order.
    Each plugin in this execution list I called step.

    To do that, in my main application code, I wrote:

    callNextPlugin()
    

    Here is the method callNextPlugin()

    void job_manager::callNextPlugin(void)
    {
       if (_currentStep + 1 > _numSteps) { // _current_step start from 0
           std::cout << "Finished: All done!" << std::endl;
           return;
       } else {
           runPlugin(_currentStep);
           _currentStep++;
       }
    }
    

    void job_manager::runPlugin(const size_t step_id)
    {
        const auto& selectedPlugin = _pluginStack[step_id];
    
        QThread* thread = new QThread;
        selectedPlugin->moveToThread(thread); // Runs Ok for the first time, but I got a error in the second time.
    
        connect(selectedPlugin, &Plugin_API::sendMsg, this, &job_manager::pluginMessage);
        connect(selectedPlugin, &Plugin_API::finished, thread, &QThread::quit);
        connect(selectedPlugin, &Plugin_API::finished, selectedPlugin, &Plugin_API::deleteLater);
        connect(selectedPlugin, &Plugin_API::finished, this, &job_manager::callNextPlugin);
        connect(thread, &QThread::started, selectedPlugin, &Plugin_API::start);
        connect(thread, &QThread::finished, thread, &QThread::deleteLater);
    
        thread->start(QThread::NormalPriority);
    }
    

    This code runs ok when I have just a single plugin (step_id = 0) in the execution list.
    But when I have 2 plugins...or the same plugin twice.... I got this run-time error below:

    QObject::moveToThread: Cannot move objects with a parent
    

    Could you help me?
    @CP71 , any ideas?



  • if we want to be picky then connect(selectedPlugin, &Plugin_API::finished, selectedPlugin, &Plugin_API::deleteLater); will cause _pluginsMap to contain dangling pointers (unless it's a QMap<QString, QPointer<Plugin_API> > then it's just null pointers)


  • Qt Champions 2019

    @fem_dev said in QThread - moveToThread: Cannot move objects with a parent:

    selectedPlugin

    This object has a parent which lives in the current thread so it can not be moved. Therefore un-parent it (or create it without a parent) and move it then.



  • @Christian-Ehrlicher thank you, but I'm lost in this concept of parent here. Could you help me a little more?

    In my case, each selectPlugin is generated from the Plugin class.
    The Plugin class uses the Plugin_API class to interface.

    Here is the Plugin class:

    #ifndef PLUGIN_H
    #define PLUGIN_H
    
    #include <iostream>
    
    #include <QObject>
    #include <QtPlugin>
    #include <QtCore/qglobal.h>
    
    #if defined(PLUGIN_LIBRARY)
    #  define PLUGIN_EXPORT Q_DECL_EXPORT
    #else
    #  define PLUGIN_EXPORT Q_DECL_IMPORT
    #endif
    
    #include "plugin_api.h"
    
    class PLUGIN_EXPORT Plugin: public Plugin_API
    {
        Q_OBJECT
        Q_PLUGIN_METADATA(IID "com.rotortest.plugin")
        Q_INTERFACES(Plugin_API)
    
    public:
        explicit Plugin();
        ~Plugin() override;
    
    private:
        void process() override;
    };
    
    #endif // PLUGIN_H
    
    

    Here is the Plugin_API class (shared library interface):

    #ifndef PLUGIN_API_H
    #define PLUGIN_API_H
    
    #include <QObject>
    
    class Plugin_API : public QObject
    {
        Q_OBJECT
    
    private:
    
    public:
        Plugin_API() 
        {
        }
    
        virtual ~Plugin_API() = default;
    
        void start() { 
            // Some code ...
        }
    
    public slots:
        virtual void process() = 0;
    
    signals:
        void finished();
    };
    
    // Declare our interface:
    Q_DECLARE_INTERFACE(Plugin_API, "com.rotortest.plugin")
    
    #endif // PLUGIN_API_H
    

    I need the Plugin_API always to interface the plugin with the main app.

    How can I fix it?



  • @Christian-Ehrlicher , I don't know why, but now my error message has change to:

    QObject::moveToThread: Current thread (0x7fa149605d40) is not the object's thread (0x7fa14ae4f3b0).
    Cannot move to target thread (0x7fa1494d0bb0)
    
    You might be loading two sets of Qt binaries into the same process. Check that all plugins are compiled against the right Qt binaries. Export DYLD_PRINT_LIBRARIES=1 and check that only one set of binaries are being loaded.
    18:27:58: The program has unexpectedly finished.
    

    Any ideas?


  • Qt Champions 2019

    @fem_dev said in QThread - moveToThread: Cannot move objects with a parent:

    Any ideas?

    I already gave you the answer - don't set a parent or unparent it.



  • @fem_dev said in QThread - moveToThread: Cannot move objects with a parent:

    QObject::moveToThread: Current thread (0x7fa149605d40) is not the object's thread (0x7fa14ae4f3b0).

    Error is pretty clear. If you already moved a plugin to a thread you can't pull it from the thread you put it in and push it into another. moveToThread is push only so only the thread that owns the object can move it to another one



  • @Christian-Ehrlicher sorry...I'm not identifying the parent in this case.

    I have 3 elements in this code:

    • QThread
    • Plugin Class
    • Plugin_API Class (Interface)

    I didn't set a parent in any of these 3 elements. You can see it in the code above.

    So:
    1- Who is the parent and the children here?
    2- How can I "don't set a parent"?
    3- How can I "unparent"?



  • @VRonin Thank you, but I'm newer in the QThread...and my english is not so good.

    Could you please give me a simple example? May be a pseudo-code or a sequence that I need to do to fix it?

    I appreciate your help and support.



  • @fem_dev said in QThread - moveToThread: Cannot move objects with a parent:

    I didn't set a parent in any of these 3 elements. You can see it in the code above.

    Actually the code above is pretty lacking in this sense. we have no information regarding how you fill _pluginStack.

    How can I "unparent"?

    call _pluginStack[step_id]->setParent(nullptr);

    @fem_dev said in QThread - moveToThread: Cannot move objects with a parent:

    Could you please give me a simple example? May be a pseudo-code or a sequence that I need to do to fix it?

    It's not clear what you are trying to do here. seems like you are creating a dedicated thread for each plugin but then you try to move those plugins in other threads, hard to give you an example if we don't know what you want to do. In this recent thread there are some examples of working with threads but I'm not 100% sure it applies to your case



  • @VRonin I will try to explain better...

    Here is how I load each plugin using QPluginLoader where the input argument called plugins is a list of shared libraries absolute path.

    void job_manager::load_plugins(const QStringList plugins)
    {
        foreach(QString file, plugins) {
    
            QPluginLoader loader(file);
            if (loader.load()) {
                qInfo() << "Loaded: " << loader.fileName();
                Plugin_API* plugin = qobject_cast<Plugin_API*>(loader.instance());
    
                if (plugin) {
                     // Store plugin in a QMap<std::string, Plugin_API*>
                    _pluginsMap[plugin->getName()] = plugin;
                } else {
                    qCritical() << "Wrong Plugin API! Could not cast: " << loader.fileName();
                }
            } else {
                qCritical() << "ERROR: " << loader.fileName() << " - Msg: " << loader.errorString();
            }
        }
    }
    

    In the UI, I have a button called "run". Here is the onClick() action:

    void job_manager::on_run_btn_clicked()
    {
        // ===== Fill the plugin stack vector ===== //
        _numSteps = ui->added_list->count(); // UI execution plugin list. Each plugin is added by the user
        _pluginStack.reserve(_numSteps); // std::vector<Plugin_API*>
    
        for (size_t s = 0; s < _numSteps; s++) {
            // Get the plugin name:
            const std::string pluginName = ui->added_list->item(s)->text().toStdString();
    
            // Store the plugin in a vector. 
            // I do that because the user can run the same plugin multiple times in a user defined execution sequence.
            // Always in the serial order
            _pluginStack[s] = _pluginsMap[pluginName];
        }
    
        callNextPlugin();
    }
    

    So, my _pluginMap can be something like:

    ---------------------
    | ["A"] => Plugin A |
    | ["B"] => Plugin B |
    ---------------------
    

    And, my _pluginStack vector can be something like:

    | 0 | Plugin A |
    | 1 | Plugin A |
    | 2 | Plugin B |
    | 3 | Plugin A |
    

    As you can see above, inside the job_manager::on_run_btn_clicked() method, I call the callNextPlugin().

    void job_manager::callNextPlugin(void)
    {
       if (_currentStep + 1 > _numSteps) { // _current_step start from 0
           std::cout << "Finished: All done!" << std::endl;
           return;
       } else {
           runPlugin(_currentStep);
           _currentStep++;
       }
    }
    

    And here is the final runPlugin() method:

    void job_manager::runPlugin(const size_t step_id)
    {
        const auto& selectedPlugin = _pluginStack[step_id];
    
        QThread* thread = new QThread;
        selectedPlugin->moveToThread(thread); // Runs Ok for the first time, but I got a error in the second time.
    
        connect(selectedPlugin, &Plugin_API::sendMsg, this, &job_manager::pluginMessage);
        connect(selectedPlugin, &Plugin_API::finished, thread, &QThread::quit);
        connect(selectedPlugin, &Plugin_API::finished, selectedPlugin, &Plugin_API::deleteLater);
        connect(selectedPlugin, &Plugin_API::finished, this, &job_manager::callNextPlugin);
        connect(thread, &QThread::started, selectedPlugin, &Plugin_API::start);
        connect(thread, &QThread::finished, thread, &QThread::deleteLater);
    
        thread->start(QThread::NormalPriority);
    }
    

    So, when one plugin finish your job, the job_manager receives a signal &Plugin_API::finished and it calls the callNextPlugin() method.

    I added the _pluginStack[step_id]->setParent(nullptr); right after the moveToThread() line, but I got the same run-time error.

    QObject::moveToThread: Current thread (0x7fdf56f056e0) is not the object's thread (0x7fdf56e39c80).
    Cannot move to target thread (0x7fdf582ba950)
    
    You might be loading two sets of Qt binaries into the same process. Check that all plugins are compiled against the right Qt binaries. Export DYLD_PRINT_LIBRARIES=1 and check that only one set of binaries are being loaded.
    

    Please, could you help me a little bit more? How can I fix it?



  • I added the _pluginStack[step_id]->setParent(nullptr); right after the moveToThread() line, but I got the same run-time error.

    It should be added before


    This running through workers is exactly the case described in the topic linked above, have a look at the last 2 examples to avoid unecessary operations

    QObject::moveToThread: Current thread (0x7fdf56f056e0) is not the object's thread (0x7fdf56e39c80).

    This error is saying that you called moveToThread on that plugin already. The main thread (0x7fdf56f056e0) sent the object to a secondary thread (0x7fdf56e39c80) already so it can't send it to 0x7fdf582ba950. only 0x7fdf56e39c80 can send the object to 0x7fdf582ba950



  • @VRonin I tried to put the _pluginStack[step_id]->setParent(nullptr); before the moveToThread() too, but i got the same error message.

    I will try to look better the topic that you send to me.

    If I have doubts, I will come back here, ok?



  • @fem_dev
    Hi,
    how our colleagues have already said a object has a parent which lives in the current thread so it cannot be moved!

    I don’t understand why you wrote:
    const auto& selectedPlugin = _pluginStack[step_id];

    instead of
    auto& selectedPlugin = _pluginStack[step_id];

    https://www.programming-books.io/essential/cpp/auto-const-and-references-b0a629204f0a4ed6a8b4b02f30835c31

    I'm wondering why the first instance runs well and then fail, or it seems so.
    What difference are there? If I'd be you I'd try to find the differences between two plugins creation.



  • if we want to be picky then connect(selectedPlugin, &Plugin_API::finished, selectedPlugin, &Plugin_API::deleteLater); will cause _pluginsMap to contain dangling pointers (unless it's a QMap<QString, QPointer<Plugin_API> > then it's just null pointers)



  • @CP71 There's no difference. You are right.



  • @VRonin Yes...I think that this is my problem. Thank you!


Log in to reply