Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Is there a way to wait on the current thread?



  • I see there is QWaitCondition, but that is for waiting on another thread. What if I want to just tell Qt, "Keep processing things in your event queue, but resume here when some condition is met?"

    Do I have to wire up more signals and slots, and chain them together? Or is there a cleaner way that is more akin to a condition variable?



  • Sounds like a QEventLoop.
    But it will somehow block the thread nevertheless.
    If you really want Qt to "keep processing things", asynchronization like signals and slots may still be the best way.



  • You can use QThread::finished() signal for that.



  • @gde23 said in Is there a way to wait on the current thread?:

    You can use QThread::finished() signal for that.

    According to the docs that is for when the thread is finished executing. I don't think that's what I am looking for. Perhaps an example would help?

    Here is what I need:

    class Client : public QObject
        {
        Q_OBJECT
        public:
            explicit Client(const QUrl &url);
            ~Client();
    
            void connect();
            void send(const std::string & message);
    
        private Q_SLOTS:
    
            void onConnected();
            void onClosed();
            void onError(QAbstractSocket::SocketError errorCode);
    
        private:
            QWebSocket * m_webSocket;
            QUrl m_url;
        };
    
    Client::Client(const QUrl &url)
            :
            QObject(),
            m_url(url)
        {
            m_webSocket = new QWebSocket();
            QObject::connect(m_webSocket, &QWebSocket::connected, this, &Client::onConnected);
            QObject::connect(m_webSocket, &QWebSocket::disconnected, this, &Client::onClosed);
            QObject::connect(m_webSocket, QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error), this, &Client::onError);
        }
    
        Client::~Client()
        {
            delete m_webSocket;
        }
    
        void Client::connect()
        {
            m_webSocket->open(m_url);
        }
    
        void Client::send(const std::string & message)
        {
            qint64 numbytesWritten = m_webSocket->sendTextMessage(QString::fromStdString(message));
            std::cout << "Wrote " << numbytesWritten << " bytes" << std::endl;
        }
    
        void Client::onConnected()
        {
            std::cout << "Connected" << std::endl;
            // Fire some kind of condition variable type mechanism here to tell us back in main that we have been connected and the next statement can execute.
        }
    
        void Client::onClosed()
        {
            std::cout << "Closed" << std::endl;
        }
    
        void Client::onError(QAbstractSocket::SocketError errorCode)
        {
            std::cout << "Error: " << errorCode << std::endl;
        }
    
    int main(int argc, char ** argv)
        {
            QCoreApplication app(argc, argv);
            QUrl url("ws://echo.websocket.org");
            Client client(url);
            client.connect();
    
            // Rather than having send in the slot the client had set up for connect, I want it here, after a condition_variable type wait, which would be tripped by the slot for connect. However, the connect slot doesn't happen on another thread, it happens on this thread, because everything is running in the event queue set up by QApplication
            // wait(connectedCV, 5000);
            client.send("Test message");
    
            return app.exec();
        }
    


  • I've never used the main application for anything other than setting up the main application parameters and things related to the init of the program itself, so probably not the best to answer this, but setting a bool as the condition value, etc connected=true; then running while(!client.connected) in the main loop should work, though probably freeze until it connects..


  • Lifetime Qt Champion

    Emit a signal in onConnected() and send your data in a slot which is connected to your signal.



  • @Christian-Ehrlicher , best answer, but he said: Rather than having send in the slot the client had set up for connect, I want it here


  • Lifetime Qt Champion

    @MEMekaniske said in Is there a way to wait on the current thread?:

    but he said: Rather than having send in the slot the client had set up for connect, I want it here

    Bad design which fits not into the Qt way of doing things. Or in other words: don't use Qt if you don't want to use it's mechanisms.



  • @Christian-Ehrlicher I agree.. Just wanted to have it said :p

    I've gone crazy with slots and signals after learning to use it. Using java and androids services and intents etc was just a mess in comparison :p



  • @Christian-Ehrlicher I don't think I'd call it a bad design.

    For example, show me how you are going to unit test this while relying on a slot for every test case. You'd rather call send in the test case itself, after being assured that connect happened.

    Currently, I am relying on qWait, but that seems wasteful, because I am waiting some amount of time, akin to a sleep, without being woken up when the thing I am waiting on happened. As a result, the unit tests are way longer than they need to be, on every automated build.

    I did find a method in the base socket class "waitOnConnected", which will, I think achieve what I am looking for with the connect call. But I was looking for something more general that could be applied to more asynchronous operations than the call to connect.


  • Lifetime Qt Champion

    @Fleshbits said in Is there a way to wait on the current thread?:

    For example, show me how you are going to unit test this while relying on a slot for every test case.

    See QSignalSpy.

    In your normal program use signals and slots as it's designed to be used.



  • C++ used to not have any direct support for suspending a function and resume it later. This has to do with how the call stack works. Hence, Qt does not provide any direct means for this. What might actually work is writing a small loop which exits when connected and repeatedly calls QApplication::processEvents(). This is basically your own small event loop.

    C++20 provides the feature you are requesting, but you should not expect that it ties in very well with Qt's signals and slots. It is called coroutines. These handle their stacks differently and are built so they can return and later resume where they left off. Though it will not integrate with the signals and slots directly.



  • @SimonSchroeder regarding QApplication::processEvents()

    I'm working on a bit of a intensive proccessing application now, doing serial com with 115200 baudrate, alongside 20mb/s tcp transfers, and emitting a lot of signals and slots for data processing, both backend and to qml.

    I wanted to get some faster responses and updates while either the TCP or Serial com were in the proccessing step after receiving/sending as those used some time, so I added QApplication::processEvents() a few places, and everything started to get wired... eventually crashing or freezing.. All features were also protected by a mutex but still it went dead..

    I had longer and longer periods before crash after removing the QApplication::processEvents() one by one. so for any feature running a lot of processes, it's better to just organize everything better from the start. and as everyone said above, use signals and slots.

    Edit;
    Today I checked the CPU and Memory usage of my program again, yesterday with the processEvents() my app ran at 10-20% of my CPU, today after I have removed them, it's running it's routines with just 0-5 as it should :p Starting to understand why those who are experienced in Qt says to use it only when one really need to.. :)



  • In my example, I have made a websocket implementation, with an abstract interface, that allows for a concrete implementation. One concrete implementation uses QWebsocket and another uses boost::beast. The abstract interface is the break between Qt specifics and standard C++.

    If I can't break the Qt specifics somewhere along the inheritance tree to abstract away the details, because I am required to fire a signal rather than call std::bind, then I am damning all my users to use Qt in their consuming classes and can never change out my implementation.

    I'd have to fire a signal, in order to use QSignalSpy rather than qWait, in my unit tests, in order to have them run at any kind of acceptable speed.



  • @Fleshbits If you want to use Qt you have to distribute Qt with your script or application... No way around that if you're not making changes and recompiling from the source itself...



  • @MEMekaniske I'm not sure what distributing it has to do with anything.

    In object orientated programming we often have:

    class IInterfaceToSomething
    {
    // I don't know anything about implementation details
    };

    class ConcreteClassA : public IInterfaceToSomething
    {
    // I implement the interface using Qt's QWebsocket
    };

    class ConcreteClassB : IInterfaceToSomething
    {
    // I implement the interface using boost::beast, but I do the same thing as ConcreteClassA
    } ;

    What I'm talking about is that I am going to be forced to:
    A) Make IInterfaceToSomething a QOBJECT even though ConcreteClassB has nothing to do with QT whatsoever.
    B) Use signals and slots in IIInterfaceToSomething, even though ConcreteClassB would never require it.
    C) Implement ConcreteClassB using QT's signals, slots, and types, even though it has nothing to do with Qt, because the IInterfaceToSomething has to, because ConcreteClassA does.

    I originally abstracted away the Qt specifics, by using std::bind and translating types, in ConcreteClassA where it implemented the common Interface. ConcreteClassA then used private slots to communicate with the QWebsocket it is using.

    However, if we are going to use QSignalSpy in unit tests, then instead of std::bind for ConcreteClassA to implement the interface, we now have to use signals there too, making the interface use signals, making everything derived from it use signals.

    Does that make more sense?
    Qt is invading my entire repo, because I wanted to implement one class with it.



  • @Fleshbits "Qt is invading my entire repo, because I wanted to implement one class with it."

    You cannot use a class from Qt without using all dependencies of the class as with any other library you'd find.

    Qt is made for application writing, so better just find, buy or write the class you want to use if you do not want to have all dependencies for the Qt class with your software.



  • @MEMekaniske Either you do not know what abstraction is, or are grossly not understanding what I am talking about.

    • Abstraction has nothing to do link dependencies.
    • Any good library should be written in such a way that you can abstract away the details and use an interface.
    • Modern object orientated programming incorporates techniques like dependency injection which require the above statement to be true.

    I'm not here to tell you how bad Qt sucks.

    I am here asking:
    How does one abstract away QObject::connect somewhere in their inheritance hierarchy?

    If your answer is, "You cannot" then that is an acceptable answer, but not one that should make anyone happy. It would also be one that I'd find hard to believe concerning a library that has been around for decades and is very popular in the C++ community.


  • Moderators

    @Fleshbits said in Is there a way to wait on the current thread?:

        client.connect();
    
        // Rather than having send in the slot the client had set up for connect, I want it here, after a condition_variable type wait, which would be tripped by the slot for connect. However, the connect slot doesn't happen on another thread, it happens on this thread, because everything is running in the event queue set up by QApplication
        // wait(connectedCV, 5000);
        client.send("Test message");
    

    In other words, you don't want Client::connect() return until the Client confirms that the connection is established, but you want Qt to continue processing events until the connection is established.

    @Bonnie's answer gives you what you want: Use a QEventLoop. It blocks the function without blocking the thread.

    void Client::connect()
    {
        QEventLoop innerLoop;
        connect(m_webSocket, &QWebSocket::connected,
                &innerLoop, &QEventLoop::quit)
        m_webSocket->open(m_url);
    
        // TODO: Also quit the inner event loop if the connection fails/errors
    
        // QEventLoop::exec() is a blocking function that keeps processing events and signals.
        // It returns when QEventLoop::quit() is invoked.
        // This is analogous to QCoreApplication::exec() and QCoreApplication::quit().
        innerLoop.exec();
    }
    

Log in to reply