Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Synchronously waiting for a list of signals
Forum Updated to NodeBB v4.3 + New Features

Synchronously waiting for a list of signals

Scheduled Pinned Locked Moved Unsolved General and Desktop
10 Posts 4 Posters 7.1k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • mssmM Offline
    mssmM Offline
    mssm
    wrote on last edited by
    #1

    Hi, trying something like this:
    https://falsinsoft.blogspot.com/2014/02/qt-wait-for-signal-in-synchronously-mode.html
    I need to wait synchronously for a list for signals. This is what I tried:

    SignalWait::SignalWait(QObject *object, QList<const char*> &signalList)
    {
        for(int i = 0; i < signalList.size(); ++i) {
            m_signalSpies.append(new QSignalSpy(object, signalList.at(i)));
        }
    }
    
    SignalWait::~SignalWait()
    {
        while(!m_signalSpies.isEmpty()) {
            delete m_signalSpies.takeFirst();
        }
    }
    
    QStringList SignalWait::wait(const int timeoutSec)
    {
        QTime timer(0, 0, timeoutSec);
    
        timer.start();
    
        QStringList receivedSignals;
        // mydebug(9, QString("Waiting for %1 signals").arg(m_signalSpies.size()));
        QList<QSignalSpy*>::const_iterator spyIter = m_signalSpies.begin();
    
        while((*spyIter)->isEmpty()) {
            if(timer.elapsed() > timeoutSec * 1000) {
                mydebug(5, QString("timed out after %1s without signal").arg(timeoutSec));
                return QStringList();
            }
    
            QCoreApplication::processEvents();
    
            if(++spyIter == m_signalSpies.end()) {
                spyIter = m_signalSpies.begin();
            }
        }
    
        for(spyIter = m_signalSpies.constBegin(); spyIter != m_signalSpies.constEnd(); ++spyIter) {
            if(!(*spyIter)->isEmpty()) {
                receivedSignals.append((*spyIter)->signal());
                mydebug(9, QString("Got signal %1").arg(QString((*spyIter)->signal())));
            }
        }
    
        return receivedSignals;
    }
    
    
    
    
    
    /////////////////////////////////////////////////////
           foreach(QString inFile, inFiles) {
                // # Step 1: Pending
                onFilePending(inFile);
                m_response->sendFile(pendingDir.canonicalPath() + "/" + inFile);
                SignalWait finishWaiter(m_response, QList<const char*>() << SIGNAL(fileError(const QString&))
                                                                           << SIGNAL(fileDone(const QString&)));
    
                // # Step 2: Done or Error
                QStringList receivedSignals = finishWaiter.wait(m_timeOutSec);
                foreach(QString receivedSignal, receivedSignals) {
                    if(receivedSignal.startsWith("fileDone")) {
                        onFileDone(inFile);
                    } else if(receivedSignal.startsWith("fileError")) {
                        onFileError(inFile);
                    } else {
                        myerror(QString("Waited for signal \"done\" or \"error\"  on file %1, but received signal %2")
                                    .arg(inFile).arg(receivedSignal));
                    }
    
                    if(receivedSignal.isEmpty()) {
                        myerror(QString("Waited for signal \"done\" or \"error\" on file %1, timed out after %2 seconds")
                                    .arg(inFile).arg(m_timeOutSec));
                    }
    
                }
               sleep(g_confDelay); 
            }
    

    But I always got "timed out after ..s without signal" eventhough I emitted the signal.
    Could that code work like this?

    W 1 Reply Last reply
    0
    • VRoninV Offline
      VRoninV Offline
      VRonin
      wrote on last edited by VRonin
      #2
      unsigned int signalCount = 0;
      QEventLoop waitLoop;
      const auto signalReceived = [&signalCount,&waitLoop]()->void{
      if(--signalCount==0)
      waitLoop.quit();
      }
      connect(sender1, &Sender1::signal1,signalReceived); ++signalCount;
      connect(sender2, &Sender2::signal2,signalReceived); ++signalCount;
      // ...
      connect(senderN, &SenderN::signalN,signalReceived); ++signalCount;
      waitLoop.exec();
      qDebug("All signals received");
      

      "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
      ~Napoleon Bonaparte

      On a crusade to banish setIndexWidget() from the holy land of Qt

      1 Reply Last reply
      6
      • mssmM mssm

        Hi, trying something like this:
        https://falsinsoft.blogspot.com/2014/02/qt-wait-for-signal-in-synchronously-mode.html
        I need to wait synchronously for a list for signals. This is what I tried:

        SignalWait::SignalWait(QObject *object, QList<const char*> &signalList)
        {
            for(int i = 0; i < signalList.size(); ++i) {
                m_signalSpies.append(new QSignalSpy(object, signalList.at(i)));
            }
        }
        
        SignalWait::~SignalWait()
        {
            while(!m_signalSpies.isEmpty()) {
                delete m_signalSpies.takeFirst();
            }
        }
        
        QStringList SignalWait::wait(const int timeoutSec)
        {
            QTime timer(0, 0, timeoutSec);
        
            timer.start();
        
            QStringList receivedSignals;
            // mydebug(9, QString("Waiting for %1 signals").arg(m_signalSpies.size()));
            QList<QSignalSpy*>::const_iterator spyIter = m_signalSpies.begin();
        
            while((*spyIter)->isEmpty()) {
                if(timer.elapsed() > timeoutSec * 1000) {
                    mydebug(5, QString("timed out after %1s without signal").arg(timeoutSec));
                    return QStringList();
                }
        
                QCoreApplication::processEvents();
        
                if(++spyIter == m_signalSpies.end()) {
                    spyIter = m_signalSpies.begin();
                }
            }
        
            for(spyIter = m_signalSpies.constBegin(); spyIter != m_signalSpies.constEnd(); ++spyIter) {
                if(!(*spyIter)->isEmpty()) {
                    receivedSignals.append((*spyIter)->signal());
                    mydebug(9, QString("Got signal %1").arg(QString((*spyIter)->signal())));
                }
            }
        
            return receivedSignals;
        }
        
        
        
        
        
        /////////////////////////////////////////////////////
               foreach(QString inFile, inFiles) {
                    // # Step 1: Pending
                    onFilePending(inFile);
                    m_response->sendFile(pendingDir.canonicalPath() + "/" + inFile);
                    SignalWait finishWaiter(m_response, QList<const char*>() << SIGNAL(fileError(const QString&))
                                                                               << SIGNAL(fileDone(const QString&)));
        
                    // # Step 2: Done or Error
                    QStringList receivedSignals = finishWaiter.wait(m_timeOutSec);
                    foreach(QString receivedSignal, receivedSignals) {
                        if(receivedSignal.startsWith("fileDone")) {
                            onFileDone(inFile);
                        } else if(receivedSignal.startsWith("fileError")) {
                            onFileError(inFile);
                        } else {
                            myerror(QString("Waited for signal \"done\" or \"error\"  on file %1, but received signal %2")
                                        .arg(inFile).arg(receivedSignal));
                        }
        
                        if(receivedSignal.isEmpty()) {
                            myerror(QString("Waited for signal \"done\" or \"error\" on file %1, timed out after %2 seconds")
                                        .arg(inFile).arg(m_timeOutSec));
                        }
        
                    }
                   sleep(g_confDelay); 
                }
        

        But I always got "timed out after ..s without signal" eventhough I emitted the signal.
        Could that code work like this?

        W Offline
        W Offline
        wrosecrans
        wrote on last edited by
        #3

        @mssm said in Synchronously waiting for a list of signals:

        But I always got "timed out after ..s without signal" eventhough I emitted the signal.
        Could that code work like this?

        No, not really. While you are in a while loop, no other code is running. So no other code is emitting a signal, and you aren't yielding back into the event loop for signal dispatch to happen. So after you while loop finishes, the app goes back to the event loop, then runs some code that will generate a signal. At least not without multiple threads.

        But what are you actually trying to accomplish? Spinning in a busy loop waiting for a signal looks like the wrong approach. The whole point of a signal is that you can just have the code that runs on a signal get triggered in a slot -- you don't have to sit around waiting for it yourself. Signals and busy waiting is a strange combination and definitely a code smell.

        1 Reply Last reply
        3
        • mssmM Offline
          mssmM Offline
          mssm
          wrote on last edited by
          #4

          @wrosecrans said in Synchronously waiting for a list of signals:

          While you are in a while loop, no other code is running. So no other code is emitting a signal, and you aren't yielding back into the event loop for signal dispatch to happen.

          And the QCoreApplication::processEvents() in the while loop doesn't solve that?
          Is the blogspot approach I referenced a bad idea in general?

          I know this does not conform with the basic idea of signals and slots. I tried to create a synchronous state machine. sendFile() can transmit a file via different ways, like QNetworkRequest. The result if this was successful cannot be returned directly from sendFile() directly as there are 2 signals fileError() and fileDone() emitted by different slots like onFinished() or onSslErrors().

          While processing a list of files I need to wait for exactly one of fileError() or fileDone() before continuing with the next file. Maybe I better replace the loop over list of files with an asynchronous signal slot, and continuing in some onError() and onDone() slot. Additionally I need a global timer for timeout, acting like an error after timeout.

          aha_1980A 1 Reply Last reply
          1
          • mssmM mssm

            @wrosecrans said in Synchronously waiting for a list of signals:

            While you are in a while loop, no other code is running. So no other code is emitting a signal, and you aren't yielding back into the event loop for signal dispatch to happen.

            And the QCoreApplication::processEvents() in the while loop doesn't solve that?
            Is the blogspot approach I referenced a bad idea in general?

            I know this does not conform with the basic idea of signals and slots. I tried to create a synchronous state machine. sendFile() can transmit a file via different ways, like QNetworkRequest. The result if this was successful cannot be returned directly from sendFile() directly as there are 2 signals fileError() and fileDone() emitted by different slots like onFinished() or onSslErrors().

            While processing a list of files I need to wait for exactly one of fileError() or fileDone() before continuing with the next file. Maybe I better replace the loop over list of files with an asynchronous signal slot, and continuing in some onError() and onDone() slot. Additionally I need a global timer for timeout, acting like an error after timeout.

            aha_1980A Offline
            aha_1980A Offline
            aha_1980
            Lifetime Qt Champion
            wrote on last edited by
            #5

            @mssm said in Synchronously waiting for a list of signals:

            While processing a list of files I need to wait for exactly one of fileError() or fileDone() before continuing with the next file. Maybe I better replace the loop over list of files with an asynchronous signal slot, and continuing in some onError() and onDone() slot. Additionally I need a global timer for timeout, acting like an error after timeout.

            Yeah, that sounds like a good approach and should not be too hard to implement.

            If you really want a blocking approach, you should do it in a separate thread.

            Regards

            Qt has to stay free or it will die.

            1 Reply Last reply
            1
            • mssmM Offline
              mssmM Offline
              mssm
              wrote on last edited by
              #6

              I always come back to the same issue I had in the beginning. I have a global list of pending files to process:

              while(!m_currentPendingFiles.isEmpty()) {
                      processFile(m_currentPendingFiles.takeFirst());
              

              starts the non-blocking processFile() for all files in parallel, but should actually wait for finished signal before going to the next file.

              if(m_currentPendingFiles.isEmpty()) {
                      myerror("No file to process");
                  } else {
                      processFile(m_currentPendingFiles.takeFirst());
                  }
              

              It's a command line process, that should process all files and quit. This would only send one file, but doesn't process the fileDone() signal anymore, just quits.

              aha_1980A 1 Reply Last reply
              0
              • mssmM mssm

                I always come back to the same issue I had in the beginning. I have a global list of pending files to process:

                while(!m_currentPendingFiles.isEmpty()) {
                        processFile(m_currentPendingFiles.takeFirst());
                

                starts the non-blocking processFile() for all files in parallel, but should actually wait for finished signal before going to the next file.

                if(m_currentPendingFiles.isEmpty()) {
                        myerror("No file to process");
                    } else {
                        processFile(m_currentPendingFiles.takeFirst());
                    }
                

                It's a command line process, that should process all files and quit. This would only send one file, but doesn't process the fileDone() signal anymore, just quits.

                aha_1980A Offline
                aha_1980A Offline
                aha_1980
                Lifetime Qt Champion
                wrote on last edited by
                #7

                Hi @mssm said in Synchronously waiting for a list of signals:

                if(m_currentPendingFiles.isEmpty()) {
                myerror("No file to process");
                } else {
                processFile(m_currentPendingFiles.takeFirst());
                }

                That already looks good. This piece of code should be executed

                • one time at the beginning
                • every time a file has been completely processed

                and you should be done.

                Qt has to stay free or it will die.

                1 Reply Last reply
                0
                • mssmM Offline
                  mssmM Offline
                  mssm
                  wrote on last edited by mssm
                  #8

                  Simplified workflow:

                  class FileProcessor : public QObject               
                  {
                      Q_OBJECT
                      public:
                          void process();
                      private:
                          Worker* m_worker;
                          QTimer  m_fileTimer;
                          QStringList m_currentPendingFiles;
                          
                          void processFile(const QString&);
                      private slots:
                          void onFileDone();
                          void onFileError();
                          void onFileTimeout();
                  };
                  
                  void FileProcessor::process()
                  {
                      // populating m_currentPendingFiles...
                      processFileList();
                  }
                  
                  void FileProcessor::processFileList()
                  {
                      if(m_currentPendingFiles.isEmpty()) {
                          myerror("No file to process");
                      } else {
                          processFile(m_currentPendingFiles.takeFirst());
                      }
                  }
                  
                  void FileProcessor::processFile(const QString &fileName)
                  {
                      m_worker->sendFile(m_currentFile);
                      connect(m_worker, SIGNAL(fileError()), this, SLOT(onFileError()));
                      connect(m_worker, SIGNAL(fileDone()), this, SLOT(onFileDone()));
                      connect(&m_fileTimer, SIGNAL(timeout()), this, SLOT(onFileTimeout()));
                      m_fileTimer.start(45000);   // 45 seconds
                  }
                  
                  void FileProcessor::onFileDone()
                  {
                      myinfo("File done");
                      resetFile();
                  }
                  
                  void FileProcessor::onFileError()
                  {
                      myinfo("File error");
                      resetFile();
                  }
                  
                  void FileProcessor::onFileTimeout()
                  {
                      myerror("File transmission timed out");
                      onFileError();
                  }
                  
                  void FileProcessor::resetFile()
                  {
                      m_currentFile.clear();
                      m_worker->disconnect();
                      processFileList();
                  }
                  

                  sendFile() is blocking, so the function works fine until finished. The worker actually sends some error or done signal after that which both result in a slot doing another processFileList() until the list is empty, but it doesn't call the slot anymore. I still think I need to wait for the signal, or process the main loop?

                  1 Reply Last reply
                  0
                  • mssmM Offline
                    mssmM Offline
                    mssm
                    wrote on last edited by
                    #9

                    Ok, to give you more information about the blocking sendFile(), maybe the mistake is there. It is currently blocking as it starts a QProcess and blocking:

                    Worker::Worker()
                    {
                        connect(&m_process, SIGNAL(error(QProcess::ProcessError)),      this, SLOT(onError(QProcess::ProcessError)));
                        connect(&m_process, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(onFinished(int,QProcess::ExitStatus)));
                        connect(&m_process, SIGNAL(readyReadStandardError()),           this, SLOT(onReadyReadStandardError()));
                        connect(&m_process, SIGNAL(readyReadStandardOutput()),          this, SLOT(onReadyReadStandardOutput()));
                        connect(&m_process, SIGNAL(started()),                          this, SLOT(onStarted()));
                    }
                    
                    Worker::sendFile()
                    {
                        //...
                        m_process.start(command);
                    
                        if(m_process.waitForStarted(3000) == true) {
                            m_process.write(contentsStr.toUtf8());
                            m_process.closeWriteChannel();
                    
                            if(m_process.waitForFinished(40000)) {
                                mydebug("Process finished");
                            } else {
                                mydebug("Process has not finished after timeout");
                                emit fileError();
                                // TODO: Kill process
                            }
                        } else {
                            emit fileError();
                        }
                        //...
                    }
                    

                    Maybe I should not block for QProcess started and finished.

                    1 Reply Last reply
                    0
                    • mssmM Offline
                      mssmM Offline
                      mssm
                      wrote on last edited by mssm
                      #10

                      Right, having the Worker non-blocking, it exits immediately with

                      QProcess: Destroyed while process is still running.
                      

                      This is a CLI application, I had no app.exec() in the main.cpp.

                      1 Reply Last reply
                      0

                      • Login

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved