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. Return value from slot in differnet thread
QtWS25 Last Chance

Return value from slot in differnet thread

Scheduled Pinned Locked Moved Unsolved General and Desktop
return valueslotthread
12 Posts 5 Posters 11.3k Views
  • 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.
  • McLionM Offline
    McLionM Offline
    McLion
    wrote on last edited by McLion
    #1

    Hi
    I have a function I use to init a socket in a different thread/class. I'd like to get a return value that tells me in the main/GUI thread if everything is ok. I know that slots should be void and should not be used for this, even if they seem to do it.
    Emit a signal back seems easy. However, I'd like to know the result immediately. Or should I just call it as regular function instead of using the signal/slot mechanism?
    Or is QMetaObject::invokeMethod the correct way?
    What's the best solution?
    Thanks
    McL

    kshegunovK 1 Reply Last reply
    0
    • A Offline
      A Offline
      Asperamanca
      wrote on last edited by
      #2

      If you want an immediate return value, and if you want the called slot to run in the context of the calling thread, what advantage would you have using signals and slots?

      1 Reply Last reply
      0
      • VRoninV Offline
        VRoninV Offline
        VRonin
        wrote on last edited by
        #3

        Please define "immediately" in the context of multiple threads. could you post a minimal example?

        I REALLY suspect the answer to your question will be "emit a signal"

        "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
        0
        • McLionM McLion

          Hi
          I have a function I use to init a socket in a different thread/class. I'd like to get a return value that tells me in the main/GUI thread if everything is ok. I know that slots should be void and should not be used for this, even if they seem to do it.
          Emit a signal back seems easy. However, I'd like to know the result immediately. Or should I just call it as regular function instead of using the signal/slot mechanism?
          Or is QMetaObject::invokeMethod the correct way?
          What's the best solution?
          Thanks
          McL

          kshegunovK Offline
          kshegunovK Offline
          kshegunov
          Moderators
          wrote on last edited by
          #4

          @McLion
          As @VRonin I'm in guessing mode, but if I understand correctly you just want to sync one of the threads to the other, so this might be what you're after:

          QMetaObject::invokeMethod(object, "slotName", Qt::BlockingQueuedConnection);
          

          However, I join the fellows that posted before me - please provide a clearer description of the problem.

          Kind regards.

          Read and abide by the Qt Code of Conduct

          1 Reply Last reply
          2
          • McLionM Offline
            McLionM Offline
            McLion
            wrote on last edited by
            #5

            Thank you guys for the answers.

            I think it needs to run in the context of the other thread, not in the context of the calling GUI thread, to have the socket that I am creating and binding run in the other thread.

            I'm currently not at my devPC and therefore can not post some code snippets.

            I send out messages over this socket that are immediately acknowledged by the remote server and I need to read and check the acks before sending the next message.
            I had it all in one (the main GUI) thread before and when sending multiple messages from within one function the responses from the server got read, but only after all messages from the one function had been sent and not in between the messages - due to the eventloop sequence.

            The idea is to have everything that is going out and comes back in over this socket in a separate thread to make sure it always is serviced in time and not blocked by the main GUI thread.
            I already did the same thing with the serial port I'm using and it works perfect. The difference is that I did not need/wanted the immediate confirmation of the successful creation of the new socket after it's initialization with parameters that I receive at runtime and don't have before back in the GUI thread.

            I hope I could make it somewhat clearer ;-)
            Of course, it can be solved without the immediate answer, but since I do not care that the calling thread is blocked for a moment at the time I'm calling the init, this would be a good solution for me.
            Thanks, McL

            kshegunovK 1 Reply Last reply
            0
            • McLionM Offline
              McLionM Offline
              McLion
              wrote on last edited by McLion
              #6

              I tried the invokeMethod .. builds ok, crashes at runtime.
              Adding some snippets:
              Thread creation:

              QThread* lolathread = new QThread;
              LoLaWorker* lolaworker = new LoLaWorker();
              lolaworker->moveToThread(lolathread);
              lolathread->start();
              

              From class LoLaWorker:

              public slots:
                bool InitLoLaUDPsocket(QString port);
              

              and the call:

              QMetaObject::invokeMethod(lolaworker, "InitLoLaUDPsocket", Qt::BlockingQueuedConnection, 
                                        Q_RETURN_ARG(bool, bInitSuccess), 
                                        Q_ARG(QString, port));
              

              What am I doing wrong?
              Thanks

              1 Reply Last reply
              0
              • McLionM McLion

                Thank you guys for the answers.

                I think it needs to run in the context of the other thread, not in the context of the calling GUI thread, to have the socket that I am creating and binding run in the other thread.

                I'm currently not at my devPC and therefore can not post some code snippets.

                I send out messages over this socket that are immediately acknowledged by the remote server and I need to read and check the acks before sending the next message.
                I had it all in one (the main GUI) thread before and when sending multiple messages from within one function the responses from the server got read, but only after all messages from the one function had been sent and not in between the messages - due to the eventloop sequence.

                The idea is to have everything that is going out and comes back in over this socket in a separate thread to make sure it always is serviced in time and not blocked by the main GUI thread.
                I already did the same thing with the serial port I'm using and it works perfect. The difference is that I did not need/wanted the immediate confirmation of the successful creation of the new socket after it's initialization with parameters that I receive at runtime and don't have before back in the GUI thread.

                I hope I could make it somewhat clearer ;-)
                Of course, it can be solved without the immediate answer, but since I do not care that the calling thread is blocked for a moment at the time I'm calling the init, this would be a good solution for me.
                Thanks, McL

                kshegunovK Offline
                kshegunovK Offline
                kshegunov
                Moderators
                wrote on last edited by
                #7

                @McLion said:

                I send out messages over this socket that are immediately acknowledged by the remote server and I need to read and check the acks before sending the next message.

                Just queue your outgoing messages. When their time comes, send them from the worker.

                The difference is that I did not need/wanted the immediate confirmation of the successful creation of the new socket after it's initialization with parameters that I receive at runtime and don't have before back in the GUI thread.

                Usually when writing event-driven, you'd post the request to do something (create socket) and if an error occurs you'd be notified (through a signal for that). One shouldn't go about blocking threads arbitrarily. One of the consequences of using Qt::BlockingQueuedConnection is that the caller will hang on the receiver's even loop mutex until the deferred invocation event is processed. This may be very quickly, but depending on the processing the worker does, it may be a very, very long time ...

                builds ok, crashes at runtime.

                Run your program through the debugger. Fix your code! Here's a MWE that illustrates how it works without crashing:
                test.h

                #ifndef TEST_H
                #define TEST_H
                
                #include <QObject>
                
                class TestWorker : public QObject
                {
                    Q_OBJECT
                
                public:
                    TestWorker(QObject * = nullptr);
                
                public slots:
                    qint32 blockingSlot();
                
                private:
                    qint32 counter;
                };
                
                #endif // TEST_H
                

                main.cpp

                #include <QCoreApplication>
                #include <QTimer>
                #include <QThread>
                #include <QDebug>
                
                #include "test.h"
                
                TestWorker::TestWorker(QObject * parent)
                    : QObject(parent), counter(0)
                {
                }
                
                qint32 TestWorker::blockingSlot()
                {
                    return counter++;
                }
                
                int main(int argc, char *argv[])
                {
                    QCoreApplication app(argc, argv);
                
                    QThread thread;
                    QObject::connect(&app, &QCoreApplication::aboutToQuit, &thread, &QThread::quit);
                
                    thread.start();
                
                    TestWorker worker;
                    worker.moveToThread(&thread);
                
                    QTimer::singleShot(0, [&worker] () -> void  {
                        qint32 i = 5, c;
                        while (i-- > 0)  {
                            QMetaObject::invokeMethod(&worker, "blockingSlot", Qt::BlockingQueuedConnection, Q_RETURN_ARG(qint32, c));
                            qDebug() << c;
                        }
                    });
                    QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection);
                
                    int result = QCoreApplication::exec();
                
                    thread.wait();
                
                    return result;
                }
                

                As one'd expect the above snippet outputs the numbers from 0 to 4 (inclusive) on the standard output (or whatever the debug stream is pointing to).

                Kind regards.

                Read and abide by the Qt Code of Conduct

                1 Reply Last reply
                1
                • McLionM Offline
                  McLionM Offline
                  McLion
                  wrote on last edited by
                  #8

                  I reworked the whole thing .. thought I'd like to share what I came to ..
                  I'm now using signals & slots only and it runs in a separate thread. Messages from GUI thread are queued with a signal and conformations from the server are directly handles in the commuication thread - they never need to be passed to the main GUI thread.
                  Basically works perfectly.
                  However I still have it sometimes that when a few messages are queued in short succession, some of the messages are sent out in such a short succession that the server has not yet responded - I think I'll add a minimum time gap between successive send message ...

                  1 Reply Last reply
                  0
                  • SGaistS Offline
                    SGaistS Offline
                    SGaist
                    Lifetime Qt Champion
                    wrote on last edited by
                    #9

                    Hi,

                    If I understand things correctly, your application should only process the queue when your request has been completed. In that case what about "re-starting" the queue processing when your last request is finished (taking into account both a successful and a failed query)

                    Interested in AI ? www.idiap.ch
                    Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                    McLionM 1 Reply Last reply
                    1
                    • SGaistS SGaist

                      Hi,

                      If I understand things correctly, your application should only process the queue when your request has been completed. In that case what about "re-starting" the queue processing when your last request is finished (taking into account both a successful and a failed query)

                      McLionM Offline
                      McLionM Offline
                      McLion
                      wrote on last edited by McLion
                      #10

                      @SGaist
                      Hi Samuel,
                      The Server actually is a syslog server only. The messages sent to it are for logging purpose and in it's acknowledge it returns the last internal message number so the device can detect lost messages and react on it as well as log the lost message event to the server in the next message.
                      It actually works as designed. It's only that I'm sending too fast, before the server did acknowledge and that raises the counter for lost messages. What happens actually confirms the systems function.
                      I really think I just need to add a minimum interval between sending log messages.
                      Thanks
                      Franz

                      VRoninV 1 Reply Last reply
                      0
                      • McLionM McLion

                        @SGaist
                        Hi Samuel,
                        The Server actually is a syslog server only. The messages sent to it are for logging purpose and in it's acknowledge it returns the last internal message number so the device can detect lost messages and react on it as well as log the lost message event to the server in the next message.
                        It actually works as designed. It's only that I'm sending too fast, before the server did acknowledge and that raises the counter for lost messages. What happens actually confirms the systems function.
                        I really think I just need to add a minimum interval between sending log messages.
                        Thanks
                        Franz

                        VRoninV Offline
                        VRoninV Offline
                        VRonin
                        wrote on last edited by
                        #11

                        @McLion said:

                        I really think I just need to add a minimum interval between sending log messages.

                        No. That just hides the problem under the carpet, move the program on a slower machine and boom the missed response are back.

                        • declare a QQueue<std::tuple<QString/*Method to invoke*/,QString /*Argument to the method*/ > > m_invokeQueue; and bool m_queueRunning=false; as private members of LoLaWorker
                        • declare a slot processQueue()
                        void LoLaWorker::processQueue(){
                        if (m_invokeQueue.isEmpty()){
                        m_queueRunning=false;
                        return;
                        }
                        m_queueRunning=true;
                        const auto currentItem = m_invokeQueue.dequeue();
                        QMetaObject::invokeMethod(lolaworker, std::get<0>(currentItem).toStdString().c_str(), Qt::BlockingQueuedConnection, 
                                                  Q_RETURN_ARG(bool, bInitSuccess), 
                                                  Q_ARG(QString, std::get<1>(currentItem)));
                        }
                        
                        • replace the part where you call invokeMethod with:
                        m_invokeQueue.enqueue("InitLoLaUDPsocket",port);
                        if(!m_queueRunning)
                        processQueue();
                        
                        • connect the signal that the server received the message to processQueue slot or just call it directly if you don't use signals.

                        P.S.
                        I'm 99.9999999% convinced you are doing async communication wrong, if you could post your code we'd probably be able to aim you in the right direction instead of putting band aids to this code

                        "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

                        McLionM 1 Reply Last reply
                        0
                        • VRoninV VRonin

                          @McLion said:

                          I really think I just need to add a minimum interval between sending log messages.

                          No. That just hides the problem under the carpet, move the program on a slower machine and boom the missed response are back.

                          • declare a QQueue<std::tuple<QString/*Method to invoke*/,QString /*Argument to the method*/ > > m_invokeQueue; and bool m_queueRunning=false; as private members of LoLaWorker
                          • declare a slot processQueue()
                          void LoLaWorker::processQueue(){
                          if (m_invokeQueue.isEmpty()){
                          m_queueRunning=false;
                          return;
                          }
                          m_queueRunning=true;
                          const auto currentItem = m_invokeQueue.dequeue();
                          QMetaObject::invokeMethod(lolaworker, std::get<0>(currentItem).toStdString().c_str(), Qt::BlockingQueuedConnection, 
                                                    Q_RETURN_ARG(bool, bInitSuccess), 
                                                    Q_ARG(QString, std::get<1>(currentItem)));
                          }
                          
                          • replace the part where you call invokeMethod with:
                          m_invokeQueue.enqueue("InitLoLaUDPsocket",port);
                          if(!m_queueRunning)
                          processQueue();
                          
                          • connect the signal that the server received the message to processQueue slot or just call it directly if you don't use signals.

                          P.S.
                          I'm 99.9999999% convinced you are doing async communication wrong, if you could post your code we'd probably be able to aim you in the right direction instead of putting band aids to this code

                          McLionM Offline
                          McLionM Offline
                          McLion
                          wrote on last edited by
                          #12

                          @VRonin
                          Thanks for your input - there seem to be some misunderstanding.
                          I can not and do not want to wait for the acknowledge of the server as a trigger to sent the next log message - this is against the idea.
                          Log messages need always to be sent, regardless wether the server acknowledges or not - imagine the server even does not acknowledge.

                          Every message sent has an ID# that is incremented by one for every new log message sent.
                          The server acknowledges every message with returning the ID#.

                          Looking at the network of the server with Wireshark and having incoming log messages with time stamps of the same msec - obviously the server has no time to send the acknowledge between incoming protocols. I.e. Sending #1 and # 2 in such a short sequence, obviously when sending #2 the counter for a missed message goes up to one.
                          This is inherit by this system and is by design.

                          Therefore, I'm going to implement a gap of at least a few msec between sending messages.
                          Because the messages are all handled and forwarded in the application by signals / QtEvent queues, I dont have a solid idea so far how to do this.
                          I may have to create a time gated sending queue before handing the messages over to the signal and the QtEvent queue.

                          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