QFutureWatcher's signal finished does not get emitted.



  • Initial note
    I've started on revamping a project that was initially was written using QT 5.4, and which worked just fine back then regarding signals and slots mentioned in the topic.

    ScriptEngine allocates ScriptInstances and then kickstarts them using start() method.

    Problem
    QFutureWatcher::finished() signal never seems to be emitted in my case, meaning that ScriptInstance::slotFinished() never gets executed, and there is no information pointing me in any direction of what may be wrong since there are no warnings compile time nor runtime.

    I have debugged the code and seen that the RunFunctionTask<void>::run() method calls this->reportFinished(); upon task completion but still no signal gets emitted.

    I'm currently using the latest version of QT (community edition) 5.9.3 with GCC kit running on Fedora 27. Installation of QT was made using the installer (i.e. not using Fedoras own package management).

    Any idea what may be wrong?

    Class (with necessary methods left):

    class ScriptInstance : public QObject
    {
        Q_OBJECT
      public:
        explicit ScriptInstance(const quint32& scriptInstanceId, const QString& scriptSource, const QString& scriptFileName, const QMap<QString, QVariant>& scriptArguments = QMap<QString, QVariant>{}, int scriptStartLine = 1);
    
        /**
         * @brief start - Method responsible for kickstarting the script instance.
         */
        void start();
    
        /**
         * @brief scriptExecutionThread - Worker thread that will be responsible for executing the requested script (in the background).
         */
        void scriptExecutionThread();
    
      public slots:
        /**
         * @brief slotFinished - Receiving method responsible for reporting back to ScriptEngine that execution is finished.
         */
        void slotFinished();
    
      signals:
        void signalDispose(quint32 scriptInstanceId);
    
      private:
        quint32 _scriptInstanceId;
        QString _scriptSource;
        QString _scriptFileName;
        QMap<QString, QVariant> _scriptArguments;
        int _scriptStartLine;
        QFuture<void> _scriptFuture;
        QFutureWatcher<void> _scriptWatcher;
    }
    

    Implementation:

    ScriptInstance::ScriptInstance(const quint32& scriptInstanceId, const QString& scriptSource, const QString& scriptFileName, const QMap<QString, QVariant>& scriptArguments, int scriptStartLine)
      :_scriptInstanceId(scriptInstanceId), _scriptSource(scriptSource), _scriptFileName(scriptFileName), _scriptArguments(scriptArguments), _scriptStartLine(scriptStartLine)
    {
    }
    
    void ScriptInstance::start()
    {
      qInfo() << "Executing script " << _scriptFileName << " (" << _scriptInstanceId << ")";
      connect(&_scriptWatcher, &QFutureWatcher<void>::finished, this, &ScriptInstance::slotFinished);
      _scriptFuture = QtConcurrent::run(this, &ScriptInstance::scriptExecutionThread);
      _scriptWatcher.setFuture(_scriptFuture);
    }
    
    void ScriptInstance::scriptExecutionThread()
    {
      QThread::sleep(10); // My task is a bit more fun, but for this presentation this should do. 
    }
    
    void ScriptInstance::slotFinished()
    {
      try
      {
        qInfo() << "Execution of script " << _scriptFileName << " (" << _scriptInstanceId << ") has ended.";
        // If there was any errors (i.e. exception thrown) during script execution,
        // then we'll need to transfer those exceptions from execution thread (http://doc.qt.io/qt-5/qexception.html)
        _scriptWatcher.future().waitForFinished();
        emit signalDispose(_scriptInstanceId);
      }
      catch(...)
      {
        emit signalDispose(_scriptInstanceId);
        throw;
      }
    }
    


  • 2 possible problems come to mind:

    • you call start twice, binding _scriptWatcher to another _scriptFuture
    • you are blocking the event loop in the main thread (a while(true) or something similar)


  • @VRonin
    I'm sorry to say that neither of the scenarions you mention occurs;

    1. start() is only called once per instance.
    2. Main thread is not blocked/hogged, the application as a whole is still responsive (including UI so such events gets processed just fine).

    Since last time I've bumped the QT version from 5.9.3 to 5.10 but the issue still remains.

    Any more ideas?



  • Ok, then let's make a slight change, lets move away from QtConcurrent and just use C++:

    void ScriptInstance::start()
    {
    qInfo() << "Executing script " << _scriptFileName << " (" << _scriptInstanceId << ")";
    QObject* signalObj = new QObject;
    connect(signalObj , &QObject::destroyed, this, &ScriptInstance::slotFinished);
    std::async(std::launch::async,[=]()->void{scriptExecutionThread(); delete signalObj;});
    }
    


  • @VRonin
    I've now tested your example and ScriptInstance::slotFinished() is still not called. I also have stepped through the deallocation of the signalObj in start() while having breakpoints set in the MOC-code, but still no luck.

    moc_ScriptInstance.cpp (part of it):

    void ScriptInstance::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
    {
        if (_c == QMetaObject::InvokeMetaMethod) {
            ScriptInstance *_t = static_cast<ScriptInstance *>(_o);
            Q_UNUSED(_t)
            switch (_id) {
            case 0: _t->signalDispose((*reinterpret_cast< quint32(*)>(_a[1]))); break;
            case 1: _t->slotFinished(); break;  // <-- Never called..
    

    Any idea where to look to see where all the invokes/function pointers gets registered, to be able to see if any entry for slotFinished is left out or not, or any other suggestions?

    PS. I really appreciate your input!


  • Lifetime Qt Champion

    Hi,

    What about calling signalObj->deleteLater() in place of delete?



  • @SGaist
    I've just tried that and slotFinished() is still not called.


  • Qt Champions 2017

    @0xedd1e
    Hi
    Are you running ScriptInstance in another thread? (movetothread style)
    Have you checked you can emit a signal and have a slot called in the class ?
    ( or the actual worker thread if that is the case)

    I tried Wielands code in main thread and it worked so i wondering
    if its something with emitting signals from your actual code.
    (clearly it is but i mean in general)

    So forget about async/QFutureWatcher for a moment and simply emit a signal to a slot
    on same level/class/interclass as the QFutureWatcher is running.

    Sorry if you already validated other signals do work. It was just a thought.



  • @mrjj
    I've now done the following;

    1. Added a new signal, signalDebug(), to ScriptInstance class.
    2. Connected signalDebug() to slot slotFinished() in the constructor for ScriptInstance class.
    3. In ScriptInstance::scriptExecutionThread() I emitted the signalDebug() signal.

    Result: slotFinished() is called just fine.

    FYI: I'm also back to using QtConcurrent mechanisms.


  • Qt Champions 2017

    @0xedd1e
    Ok, so signals seems to work.
    I was wondering if you needed to specify connection type
    but if some signals work, all should.

    but just for test
    connect(&_scriptWatcher, &QFutureWatcher<void>::finished, this, &ScriptInstance::slotFinished, Qt::QueuedConnection);

    Else Im a bit out of idea as all seem ok.

    Update
    I ran your code and slotFinished was called ?
    Can you try it
    https://www.dropbox.com/s/rhdk6xxk323ziqh/test_thread.zip?dl=0

    alt text



  • @mrjj
    Once I discovered the issue I experimented with different type of connections, but no change in behavior.

    The project you provided works just fine, so I guess that rules out that there is no issue(s) related to my setup at least.

    The ScriptEngine in my case is a singleton, but I don't see why that should matter in this case since all other signals/slots seems to work just fine.


  • Qt Champions 2017

    @0xedd1e
    Ok. super. Yes it means nothing wrong inside Qt/the setup.

    • ScriptEngine in my case is a singleton

    meaning its static ?

    But I agree if other signals can get be emitted (from same nesting level) then
    it seems unlikely its that.



  • @mrjj
    Correct, it is static.

    I'm really out of ideas, so I guess I have to go back to earlier changesets within my project and see if I can pinpoint the exact change that caused this behavior. However, the code connected to the script engine and script instance hasn't changed for quite a while though, so I'll try to back the project to an earlier changeset and see if there are changes elsewhere that causes this. I registered a bug in my own ticket system when I discovered this issue but marked it for check-later-when-qt-upgrades since it worked fine in QT 5.4 and earlier.

    Thanks again for all the feedback and I'll report back during the upcoming holiday.



  • @0xedd1e said in QFutureWatcher's signal finished does not get emitted.:

    @mrjj
    Correct, it is static.

    I'm really out of ideas, so I guess I have to go back to earlier changesets within my project and see if I can pinpoint the exact change that caused this behavior. However, the code connected to the script engine and script instance hasn't changed for quite a while though, so I'll try to back the project to an earlier changeset and see if there are changes elsewhere that causes this. I registered a bug in my own ticket system when I discovered this issue but marked it for check-later-when-qt-upgrades since it worked fine in QT 5.4 and earlier.

    Thanks again for all the feedback and I'll report back during the upcoming holiday.

    FYI;
    I've now gone back and tested previous versions of the project but can't seem to get it working in any of the earlier versions now using QT 5.10. So I guess something has changed in QT, but I can't figure out what though and to my knowledge I follow the recommendations according to the documentation of QT.

    Worth trying going back to using earlier versions of QT?


  • Qt Champions 2017

    @0xedd1e
    Hi I also looked at the release notes to see
    if something with Future/Concurrent was changed but nothing popped up.

    • Worth trying going back to using earlier versions of QT?

    Well for test. it would be a good test if exact same code just works in say 5.6
    and not in 5.10. But being stucked at 5.4 would be not so optimal.

    A note about the static.
    Just to be sure. the class that expects the Finished signal is a static member of some other class?
    As far as i can recall, you cant compile if you try to connect to a static class slot so im inclided to
    say that is not the issue.

    Since your mini sample works. There must be some difference in the real code. But i cant
    think of more to check.


Log in to reply
 

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