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. Why QThread::wait() never returns after invoked QThread::quit()?

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

Scheduled Pinned Locked Moved Solved General and Desktop
qthreadthreadwaitquitdeadlock
8 Posts 2 Posters 6.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.
  • F Offline
    F Offline
    Feuerteufel
    wrote on 13 Jan 2017, 09:46 last edited by
    #1

    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 ;-)

    K 1 Reply Last reply 13 Jan 2017, 16:21
    0
    • F Feuerteufel
      13 Jan 2017, 09:46

      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 ;-)

      K Offline
      K Offline
      kshegunov
      Moderators
      wrote on 13 Jan 2017, 16:21 last edited by
      #2

      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.

      Read and abide by the Qt Code of Conduct

      1 Reply Last reply
      1
      • F Offline
        F Offline
        Feuerteufel
        wrote on 13 Jan 2017, 17:57 last edited by
        #3

        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.

        1 Reply Last reply
        0
        • F Offline
          F Offline
          Feuerteufel
          wrote on 17 Jan 2017, 10:02 last edited by
          #4

          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).

          K 1 Reply Last reply 18 Jan 2017, 02:11
          0
          • F Feuerteufel
            17 Jan 2017, 10:02

            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).

            K Offline
            K Offline
            kshegunov
            Moderators
            wrote on 18 Jan 2017, 02:11 last edited by kshegunov
            #5

            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;
            }
            

            Read and abide by the Qt Code of Conduct

            1 Reply Last reply
            2
            • F Offline
              F Offline
              Feuerteufel
              wrote on 19 Jan 2017, 13:06 last edited by
              #6

              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.

              K 1 Reply Last reply 20 Jan 2017, 09:38
              0
              • F Feuerteufel
                19 Jan 2017, 13:06

                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.

                K Offline
                K Offline
                kshegunov
                Moderators
                wrote on 20 Jan 2017, 09:38 last edited by
                #7

                @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.

                Read and abide by the Qt Code of Conduct

                1 Reply Last reply
                1
                • F Offline
                  F Offline
                  Feuerteufel
                  wrote on 21 Jan 2017, 15:27 last edited by
                  #8

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

                  1 Reply Last reply
                  0

                  1/8

                  13 Jan 2017, 09:46

                  • Login

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