Aurelien
Aurelien

Reputation: 1092

Execute a "wait callback" during the wait for a std::condition_variable to be notified

I'm using a std::condition_variable this way :

void wait()
{
    std::unique_lock<std::mutex> lock(m_stateCompletedMutex);

    m_waitCondition.wait(lock, [this](){return (m_state == STATE_COMPLETED);});
}

I'm pretty happy with this, but now I would like to execute some code "during the wait" (I don't know if I can say it this way, but this is the idea) for example to update the GUI or to increment an wait counter, or anything else.

Saying we're using Qt, I've tried something like this :

void wait()
{
    std::unique_lock<std::mutex> lock(m_stateCompletedMutex);

    while (m_state != STATE_COMPLETED)
    {
        m_waitCondition.wait(lock);

        // Use this an example, it could be any code, executed in the waiting thread
        qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
    }
}

The goal here is to keep the GUI responsive (at least to system events, not user inputs) while we are waiting for a working thread to be completed.

I have two questions about this code :

1/ Is it the good way to do that, or is there a better method to execute code "while waiting"

2/ How often is executed the "waiting code" (the qApp->processEvents call in my example) ? Is it system dependent ? Or does it depends on the current CPU load or anything else ? Or should I use m_waitCondition.wait_for to ensure a minimum frequency call ?

About point 2/, I've tested to monitor it (with std::chrono::high_resolution_clock) and the delay seems to be anything between 200ms and 4000ms in my application, and I think this is a big range.

Upvotes: 2

Views: 1113

Answers (2)

ScottG
ScottG

Reputation: 793

No, this is not the right approach. You must never wait or sleep on the Main (GUI) thread in Qt (or any event-driven framework).

Rather than waiting, the GUI thread must receive an event of some sort. If everything is Qt (i.e. not some external piece of code that can't be "contaminated" with Qt-isms), then just have the other thread emit a signal when finished.

Otherwise, you can take user1034749's approach, and pass in a callback to the calculation function. That callback, in turn, can either set a flag, or in sticking with Qt's event driven nature, emit a signal that prods along your GUI to the next step (i.e. showing the result of the calculation).

Chances are, if you are calling ProcessEvents(), you're doing it wrong.

Upvotes: 1

fghj
fghj

Reputation: 9404

1/ Is it the good way to do that, or is there a better method to execute code "while waiting"

As for me "good way" to do it will be hide details of calculation architecture from GUI part of your program. For example interface may be something like this:

extern void calc_something(Params, const std::function<void ()> &end_calc_callback);

where end_calc_callback can be called in another (non GUI) thread context.

So usage will be like this

std::atomic<bool> result_ready{false};
calc_something(Params(), [&result_ready]() { result_ready = true; });
//wait result_ready

2/ How often is executed the "waiting code" (the qApp->processEvents call in my >example) ? Is it system dependent ?

You can set maximum work time for QCoreApplication::processEvents, see Qt documentation about this, how long you set timeout depend on your code and user, for example let you have such code:

std::atomic<bool> result_ready{false};
calc_something(Params(), [&result_ready]() { result_ready = true; });
ProgressWindow pw;
pw.show();
while (!result_ready)
  qApp->processEvents(QEventLoop::ExcludeUserInputEvents, timeout);

let's suppose you have in ProgressWindow timer to draw some animation, human can response to something on screen with delay let's say 200ms (https://en.wikipedia.org/wiki/Mental_chronometry) then depend on what animation you show in ProgressWindow, and user reaction you can choose timeout.

Upvotes: 0

Related Questions