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. Circular SIGNAL SLOT connection. Is it possible?
Forum Updated to NodeBB v4.3 + New Features

Circular SIGNAL SLOT connection. Is it possible?

Scheduled Pinned Locked Moved Solved General and Desktop
5 Posts 3 Posters 3.7k Views 2 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.
  • M Offline
    M Offline
    Mr.Floppy
    wrote on last edited by
    #1

    Hello!

    While playing around in Qt I recognized something, that doesn't make sense to me:

    If I have SLOT A emit SIGNAL B and then connect SIGNAL B to call SLOT A, it doesn't take long to get a segmentation fault error.
    I kind of get why that happens with recursive functions, because the every function has to be kept alive until the last one is finished.

    Why is that (apparently) the same with SIGNALs and SLOTs?
    Here is some code snippet:

    // Function definition
    void CircularSignalSlotConnection::doSomething()
    {
        // <5000 works, >6000 throws segmentation fault
        if (counter<5000)
        {
            counter++;
            emit doAgain();
        }
        else qDebug() << counter;
    }
    
    // Connect the function to call itself
        connect(this, SIGNAL(doAgain()), this, SLOT(doSomething()));
    
    // Initiate the "recursion"
        counter = 0;
        doSomething();
    

    Does the function have to be kept alive, until the emitted SIGNAL was "dealt with"?
    And even if it were like that, the signals can't be emitted faster, than the functions compute... so each signal has to be off the event loop before a new one gets emitted... I am confused.

    Can someone explain this to me?
    And can I deliberately "end" the function call to make the code above work for as long as I want?

    This is nothing I need for a specific problem I have... it's just something I want to wrap my head around.

    Kind regards,

    Mr. Floppy.

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

      Hi,

      No need for a signal here. Something along:

      if (counter<5000) {
          counter++;
          QTimer::singleShot(0, this, &doSomething);
      }
      

      would be cleaner

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

      1 Reply Last reply
      3
      • M Mr.Floppy

        Hello!

        While playing around in Qt I recognized something, that doesn't make sense to me:

        If I have SLOT A emit SIGNAL B and then connect SIGNAL B to call SLOT A, it doesn't take long to get a segmentation fault error.
        I kind of get why that happens with recursive functions, because the every function has to be kept alive until the last one is finished.

        Why is that (apparently) the same with SIGNALs and SLOTs?
        Here is some code snippet:

        // Function definition
        void CircularSignalSlotConnection::doSomething()
        {
            // <5000 works, >6000 throws segmentation fault
            if (counter<5000)
            {
                counter++;
                emit doAgain();
            }
            else qDebug() << counter;
        }
        
        // Connect the function to call itself
            connect(this, SIGNAL(doAgain()), this, SLOT(doSomething()));
        
        // Initiate the "recursion"
            counter = 0;
            doSomething();
        

        Does the function have to be kept alive, until the emitted SIGNAL was "dealt with"?
        And even if it were like that, the signals can't be emitted faster, than the functions compute... so each signal has to be off the event loop before a new one gets emitted... I am confused.

        Can someone explain this to me?
        And can I deliberately "end" the function call to make the code above work for as long as I want?

        This is nothing I need for a specific problem I have... it's just something I want to wrap my head around.

        Kind regards,

        Mr. Floppy.

        T Offline
        T Offline
        t3685
        wrote on last edited by
        #3

        @Mr.Floppy

        Check out this 2-part blog post.

        https://woboq.com/blog/how-qt-signals-slots-work.html

        If I remember correctly, if the object that emits the signal and the object with the slot connected to that signal live in the same thread: signal-slot invokations are just regular nested function calls.

        1 Reply Last reply
        1
        • M Offline
          M Offline
          Mr.Floppy
          wrote on last edited by Mr.Floppy
          #4

          @t3685
          Thank you!
          The blog-post mentions the following code snippet:

                  QObject * const receiver = c->receiver;
                  const bool receiverInSameThread = QThread::currentThreadId() == receiver->d_func()->threadData->threadId;
          
                  // determine if this connection should be sent immediately or
                  // put into the event queue
                  if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)
                      || (c->connectionType == Qt::QueuedConnection)) {
                      /* Will basically copy the argument and post an event */
                      queued_activate(sender, signal_index, c, argv);
                      continue;
                  } else if (c->connectionType == Qt::BlockingQueuedConnection) {
                      /* ... Skipped ... */
                      continue;
                  }
          

          So you are correct.
          Whenever possible, a signal will result in a simple function call.
          To be sure to post an event, you'll have to manually specify the connection type as Qt::QueuedConnection.
          When I do that in my previous example it works as intended :)

          connect(this, SIGNAL(doAgain()), this, SLOT(doSomething()),Qt::QueuedConnection);
          

          @SGaist
          Thanks to you too!
          Although your code doesn't explain anything, it also works as intended.
          Maybe because QTimer posts events innately?

          Out of curiosity I "benchmarked" all discussed methods.
          [EDIT] The first time all methods counted up the same counter and have used the same QElapsedTimer... that wasn't very smart... I have corrected that and now counting via event takes way longer than iterative counting... which makes way more sense.
          But the main point was to understand what went wrong in the first place, and that's solved.

          Thanks again for the quick help! I have learned something :)

          Kind regards,

          Mr. Floppy.

          [EDIT] Results (one method at a time):

          Iterative counting to 1000000 took me 2 ms.
          Iterative counting to 10000000 took me 25 ms.
          Iterative counting to 100000000 took me 243 ms.
          
          Queued counting to 1 took me 19 ms.
          Queued counting to 10000 took me 232 ms.
          Queued counting to 100000 took me 1947 ms.
          
          Timed counting to 1 took me 18 ms.
          Timed counting to 10000 took me 301 ms.
          Timed counting to 100000 took me 2789 ms.
          

          [EDIT] Code:

          #include "circularsignalslotconnection.h"
          #include "ui_circularsignalslotconnection.h"
          #include <QDebug>
          #include <QTimer>
          
          CircularSignalSlotConnection::CircularSignalSlotConnection(QWidget *parent) :
              QMainWindow(parent),
              ui(new Ui::CircularSignalSlotConnection)
          {
              ui->setupUi(this);
              stopCountingAt = 100000;
              // Just count like a normal person
              elapsedTimerC.restart();
              counterC = 0;
              while (counterC < stopCountingAt) ++counterC;
              qDebug() << "Iterative counting to" << counterC << "took me" << elapsedTimerC.elapsed() << "ms.";
              // Connect doSomething to itself
              connect(this, SIGNAL(doAgain()), this, SLOT(doSomething()), Qt::QueuedConnection);
              // Count via circular SIGNAL SLOT connection
              elapsedTimerA.restart();
              counterA = 0;
              doSomething();
              // Count via QTimer
              elapsedTimerB.restart();
              counterB = 0;
              doSomethingWithQTimer();
          }
          
          CircularSignalSlotConnection::~CircularSignalSlotConnection()
          {
              delete ui;
          }
          
          void CircularSignalSlotConnection::doSomething()
          {
              if (counterA < stopCountingAt)
              {
                  ++counterA;
                  emit doAgain();
              }
              else qDebug() << "Queued counting to" << counterA << "took me" << elapsedTimerA.elapsed() << "ms.";
          }
          
          void CircularSignalSlotConnection::doSomethingWithQTimer()
          {
              if (counterB < stopCountingAt) {
                  ++counterB;
                  QTimer::singleShot(0, this, &doSomethingWithQTimer);
              }
              else qDebug() << "Timed counting to" << counterB << "took me" << elapsedTimerB.elapsed() << "ms.";
          }
          
          1 Reply Last reply
          0
          • SGaistS Offline
            SGaistS Offline
            SGaist
            Lifetime Qt Champion
            wrote on last edited by
            #5

            A QTimer in single shot mode with a 0 timeout means that your slot will be called next time the event loop runs. This means that your application is still "alive". When using a while loop like that, you are blocking the event loop from processing.

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

            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