Why QThread::wait() never returns after invoked QThread::quit()?



  • I have a problem, that QThread never returns from wait().
    The scenario contains 2 additional threads (QThread and std::thread) besides the main execution thread. Let's call the QThread Q, the std::thread T and the main thread M.
    In M I create Q, the Receiver-object R "living" in Q and the Sender-object S. Also a std::thread T is created executing a bunch if emits with S.

    class Sender : public QObject
    {
      Q_OBJECT;
    public:
      std::vector<int> m_Sent;
    
      Sender()
      {
      }
    
    public slots:
    signals:
      void signal(int i);
    
    public:
      void send(int i)
      {
        m_Sent.emplace_back(i);
        emit signal(i);
      }
    };
    
    
    class Receiver : public QObject
    {
      Q_OBJECT;
    public:
      std::vector<int> m_Received;
    
      Receiver()
      {
      }
    
      void Connect(Sender* s)
      {
        connect(s, &Sender::signal, this, &Receiver::slot, Qt::QueuedConnection);
      }
    
      void Disconnect(Sender* s)
      {
        disconnect(s, &Sender::signal, this, &Receiver::slot);
      }
    
    public slots:
      void slot(int i)
      {
        m_Received.emplace_back(i);
      }
    
    };
    
    void main(int argc, char** argv)
    {
      QApplication app(argc, argv);
      qint64 random_seed = QDateTime::currentMSecsSinceEpoch();
      std::cout << "Setting random seed " << random_seed << "\n";
      std::srand(random_seed);
      std::unique_ptr<Receiver> R(new Receiver);
      std::unique_ptr<Sender> S(new Sender);
      auto actions = [&S]() {
        int i = 0;
        std::chrono::steady_clock::time_point current =
                std::chrono::steady_clock::now();
        std::chrono::steady_clock::time_point finish =
                current + std::chrono::milliseconds(100);
        while (current < finish)
        {
          std::this_thread::sleep_for(std::chrono::microseconds(std::rand()%1000));
          S->send(i++);
          std::this_thread::sleep_for(std::chrono::microseconds(std::rand()%1000));
          S->send(i++);
          std::this_thread::sleep_for(std::chrono::microseconds(std::rand()%1000));
          S->send(i++);
          std::this_thread::sleep_for(std::chrono::microseconds(std::rand()%1000));
          S->send(i++);
          std::this_thread::sleep_until(current + std::chrono::milliseconds(17));
          current = std::chrono::steady_clock::now();
        }
      };
    
      std::unique_ptr<QThread> Q(new QThread());
      R->moveToThread(Q.get());
      R->Connect(S.get());
      Q->start();
      std::thread T(actions);
      T.join();
      QMetaObject::invokeMethod(Q.get(), "quit", Qt::QueuedConnection);
      Q->wait();// waits forever
    
      std::cout << "Sent:     ";
      for(auto v : S->m_Sent)
      {
        std::cout << v << " ";
      }
      std::cout << std::endl;
      std::cout << "Received: ";
      for(auto v : R->m_Received)
      {
        std::cout << v << " ";
      }
      std::cout << std::endl;
    }
    

    I'm working on Windows with VS2013 and Qt 5.5.1. While debugging I went through all emits so all should be inserted to event loop in Q. Also I tried to use a BlockingQueuedConnection for the invoked quit() and removed the wait() but then I got a Qt-warning/error of a detected Deadlock, but I don't use any locks, so seems to be some Qt-internal stuff I have no insight so far. Thanks for some enlightment on the problem ;-)


  • Qt Champions 2016

    Sweet Mary ... that's one big pile of a mess ... I had to read it 3 times to get what's happening.
    Here:

    QMetaObject::invokeMethod(Q.get(), "quit", Qt::QueuedConnection);
    

    This posts an event to the QThread's event loop (which is the main event loop), so the event is never processed as you're blocking the main event loop by calling QThread::wait().

    Q.get()->quit(); 
    

    should work just fine.



  • There is no main event loop and that is intended. There is only one event loop in the thread handled by QThread Q. I tried to call just Q.get()->quit() but than it happens that not all sent events are really handled. I need a way to put the quit() at the end of the event loop in Q and afterwards waiting in the main thread that Q is finished. But thanks for your response.



  • Still no solution? Doesn't Qt provide a way to insert kind of quit statement in the eventloop of the thread (not the main eventloop).


  • Qt Champions 2016

    There is no main event loop and that is intended.

    Sure there is, as long as you have a QApplication object, you have a main event loop, you just don't spin it.

    Still no solution? Doesn't Qt provide a way to insert kind of quit statement in the eventloop of the thread (not the main eventloop).

    Not really, no, and there's no need too. You have that problem because of your mixing of event-driven and iteration based threads, but you can easily roll your own solution. Example follows:

    class Sender : public QObject
    {
        // ...
    signals:
        void signal(int i);
        void finished();
    
        // ...
    };
    
    int main(int argc, char** argv)
    {
        // ...
        auto actions = [&S]() {
            // ....
            S->finished();
        };
    
        std::unique_ptr<QThread> Q(new QThread());
        QObject::connect(S.get(), &Sender::finished, R.get(), [&Q] () { Q.get()->quit(); });
        // ...  
        T.join();
        Q->wait();
        // ...
        return 0;
    }
    


  • Yeah right, the main loop exists but is not running, that's what I meant but didn't write it correctly. The instantiation of QApplication was needed ( why ever?) to get a event loop running for the QThread, but I don't need to execute the main loop.
    Thanks for your solution, that works not exactly in my case, because I can't change the Sender object (API is fixed). But it can easily be adjust, because the source of the finished signal can be any QObject, e.g. Receiver itself or do I run in another pitfall with that approach?

    //...
    QObject::connect(R.get(), &Receiver::finished, R.get(), [&Q] () { Q->quit(); });
    //...
    T.join();
    emit R->finished();
    Q->wait();
    //...
    

    Am I lost if I can't change Sender and Receiver types? Meaning is the only solution always to have some QObject connected to a lambda calling Q->quit() in context of R?
    Again thanks for your help, enlightend my understanding of how Qt works inside a little bit. As you may notice I'm testing how Qt works in context outside the Qt universe.


  • Qt Champions 2016

    @Feuerteufel said in Why QThread::wait() never returns after invoked QThread::quit()?:

    The instantiation of QApplication was needed ( why ever?)

    Because Qt is a complex system, and although you don't see it, underneath there are a zillion global variables that need to be initialized. So QApplication should be the first QObject that comes into existence (if you rely on anything QObject related that is).

    But it can easily be adjust, because the source of the finished signal can be any QObject, e.g. Receiver itself or do I run in another pitfall with that approach?

    No, the receiver can emit the signal, provided it knows when the sequence of w/e tasks is finished. Most of the time this isn't true however, so that is the "pitfall". I find your direct call to a signal a bit dubious though. It isn't wrong (syntactically), but the point of signals is they belong to an object, which object itself signals the world about things that happened ...

    Am I lost if I can't change Sender and Receiver types?

    Is deriving from them not an option?

    Meaning is the only solution always to have some QObject connected to a lambda calling Q->quit() in context of R?

    Well, I'd put it in a private slot, but technicalities aside, mostly, yes.



  • Ok, thanks, I think I have all information I need now.



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