QMetaObject::invokeMethod with BlockingQueuedConnection hangs forever



  • Hello,

    In my application I have a main thread and a worker thread (done with moveToThread). The woker thread runs in a loop which collects some data. What I want to do is to call a getter for this woker data from the main thread. So I called it like this:

    qRegisterMetaType<Data>("Data");
    Data d;
    QMetaObject::invokeMethod(worker_, "getData", Qt::BlockingQueuedConnection,
                              Q_RETURN_ARG(Data, d),
                              Q_ARG(QString,   arg));
    

    Where Data getData(QString arg) is a public slot in the worker object.

    The problem is that this hangs forever (not sure if forever but a minute is already enough, I need it immediately). Calling it with DirectConnection makes it execute in the main thraed which I don't want to. So what is the problem here? It never reaches the getData() method. Is there something else which I could use for such an asynchronous data polling?



  • does the worker thread run an event loop? if so, are you blocking it with an infinite loop?

    Looks like the invokeMethod never gets picked up by the worker loop



  • The main thread started with

    QCoreApplication a(argc, argv);
    return a.exec();
    

    and in the main thread the worker was started with

    worker->moveToThread(thread);
    thread->start();
    

    Then the worker starts the infinite loop, yes. I thought that this is somehow handled by Qt, so that the worker's methods can be invoked although it's running in a loop. But probably I was wrong, or was I?



  • @sykac said in QMetaObject::invokeMethod with BlockingQueuedConnection hangs forever:

    Then the worker starts the infinite loop

    Usually this is not necessary but yes, yhis is the cause of your invoke not working



  • @sykac
    Hi there, i am not expert with QThread but have you tried to use a request and response signals to invoke and receive the data from your worker thread to main thread ?

    Obs: I don't know if this is the best way to request data from thread.


  • Qt Champions 2017

    @sykac said in QMetaObject::invokeMethod with BlockingQueuedConnection hangs forever:

    Then the worker starts the infinite loop, yes.

    What infinite loop, can you show us?
    If you block the event loop then that means you can't process queued events, thus your call with BlockingQueuedConnection will just hang there until the actual event posted in the event queue is processed.

    @KillerSmath said in QMetaObject::invokeMethod with BlockingQueuedConnection hangs forever:

    Obs: I don't know if this is the best way to request data from thread.

    It is, indeed.



  • @kshegunov
    Okay, i told it because i readed some topics about send data using signals and noticed that it's not recommended to send data in signals because they makes a copy of data to send as parameter and another copy is made at slot, and make copy can takes time depending of data size.


  • Qt Champions 2017

    That's why Qt leverages implicit sharing to begin with. When you have the case that the signal/slot parameter(s) have to copy big-ish data chunks, then it's time to use QSharedData and QSharedDataPointer. This way you get the shallowest copies possible - i.e. a pointer being copied, and whenever you have a write operation the data would detach if it's shared.



  • @KillerSmath said in QMetaObject::invokeMethod with BlockingQueuedConnection hangs forever:

    use a request and response signals to invoke and receive the data from your worker thread to main thread ?

    Even this solution requires a non-blocked event loop running on the worker-thread side

    @kshegunov said in QMetaObject::invokeMethod with BlockingQueuedConnection hangs forever:

    whenever you have a write operation the data would detach if it's shared.

    Worth mentioning in this context that it is thread safe as long as const means thread safe or you use QExplicitlySharedDataPointer to make thread safe.

    I'm not that knowledgeable of Qt internals to assure you but I'd guess most if not all the Qt classes are usable without thinking about copying


  • Qt Champions 2017

    @VRonin said in QMetaObject::invokeMethod with BlockingQueuedConnection hangs forever:

    Worth mentioning in this context that it is thread safe as long as const means thread safe or you use QExplicitlySharedDataPointer to make thread safe.

    Eh, what? I did not at all mention thread safety. But in any case so we clear the issue here - no, the data is not thread-safe. The only thing that is thread-safe (sort of) in the whole scheme is the data copying, which happens behind the scenes. And this is just so you can delay the actual copy as long as possible, and to ensure that your objects behave correctly when it comes to reentrancy (if you have statics the object will still not be reentrant). This in no way guards the actual data from concurrent access! (bold!)



  • Thanks for suggestions guys.

    @kshegunov My infinite loop is while(flag) doStuff();, so it's definitely because of it. So if doStuff() was connected to a timer and wasn't in the while loop, it would work?

    But I'll probably stick with the object in the main thread simply calling the method of the worker which will be executed in the main thread. And as you're mentioning thread safety and data manipulation - is this safe?



  • @kshegunov said in QMetaObject::invokeMethod with BlockingQueuedConnection hangs forever:

    I did not at all mention thread safety.

    I know, I just brought it up as this thread was about multithreading

    @kshegunov said in QMetaObject::invokeMethod with BlockingQueuedConnection hangs forever:

    This in no way guards the actual data from concurrent access!

    What I meant is that if type T is reentrant, using QSharedData does not break reentrancy as both the reference counter and detach() are thread safe.
    If you have const methods that actually change the internal state then you still have a race condition (as in this case const != thread safe)



  • @sykac

    Remember, it is not a good practice to use an infinite loop in a thread, but you can solve your problem usingprocessEventsinside your loop to force your worker to process the events.

    Below is an example of how you can implement:

    worker.h

    #ifndef WORKER_H
    #define WORKER_H
    
    #include <QObject>
    #include <QMutex>
    
    class Worker : public QObject
    {
        Q_OBJECT
    public:
        explicit Worker(QObject *parent = nullptr);
        ~Worker();
    
    signals:
        // get data using signals
        void requestData();
        void responseData(const int number);
        
        void finished();
    
    public slots:
        void process();
        void stop();
        int getData();
    
    private:
        QMutex m_mutex;
        bool m_stop;
        int m_data;
    
        void doStuff();
    };
    
    #endif // WORKER_H
    

    worker.cpp

    #include "worker.h"
    #include <QCoreApplication>
    //#include <QMutexLocker>
    
    Worker::Worker(QObject *parent) : QObject(parent)
    {
        m_data = 0;
        m_stop = false;
    
        connect(this, &Worker::requestData, this, [this](){ // if prefer to use signals to return data
            // checksum or processing the data
            // ...
            // ...
            // done checksum or processing
            emit responseData(m_data);
        });
    }
    
    Worker::~Worker(){
    //    m_mutex.lock();
    //    if(!m_stop)
    //        m_stop = true;
        //    m_mutex.unlock();
    }
    
    int Worker::getData()
    {
        return m_data;
    }
    
    void Worker::stop(){
        m_mutex.lock();
        if(!m_stop)
            m_stop = true;
        m_mutex.unlock();
    }
    
    void Worker::process()
    {
        m_stop = false;
    
        while(!m_stop)
        {
            doStuff();
            QCoreApplication::processEvents(QEventLoop::AllEvents); // force the process of events among each iteration
        }
    
        emit finished();
    }
    
    void Worker::doStuff()
    {
        // QMutexLocker locker(&m_mutex); // if necessary for security
        m_data = (m_data + 1) % 101;
    }
    

    your_application constructor

    ...
        newThread = new QThread();
        newWorker = new Worker();
        newWorker->moveToThread(newThread);
        
        connect(newThread, &QThread::started, newWorker, &Worker::process);
        connect(newWorker, &Worker::finished, newWorker, &Worker::deleteLater);
     
        newThread->start();
    
       // using signals to request and response data
    
        connect(newWorker, &Worker::responseData, [this](const int data){
            qDebug() << "Data Received:" << data;
        });
    
        QTimer::singleShot(5000, newWorker, &Worker::requestData);
    
        // or calling method from worker to return data
    
       int d;
       QMetaObject::invokeMethod(newWorker, "getData", Qt::BlockingQueuedConnection,
                                  Q_RETURN_ARG(int, d));
    
       qDebug() << d;
    
       
    ...
    

    your_application destructor

    ...
        newWorker->stop();
    
        newThread->quit();
        newThread->wait();
        
        delete newThread;
    ...
    

  • Qt Champions 2017

    @sykac said in QMetaObject::invokeMethod with BlockingQueuedConnection hangs forever:

    So if doStuff() was connected to a timer and wasn't in the while loop, it would work?

    Yes.

    And as you're mentioning thread safety and data manipulation - is this safe?

    Is what safe? If you use the data from one thread only then it's safe, yes.

    @KillerSmath said in QMetaObject::invokeMethod with BlockingQueuedConnection hangs forever:

    Below is an example of how you can implement

    Is the mutex really necessary to guard a simple boolean? I'd suggest substituting that with an atomic, even if Qt's mutex implementation is rather fast.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.