QWaitCondition and Signals
-
Hi all,
I have a problem with a QWaitCondition and Signals sent right before the thread is waiting.
What can happen in the following situation? Is it possible that the signal is not sent immediately before the thread waits?
Do I have to use Qt::BlockingQueuedConnection to setup the signal?// in class Device: ... LightControl* lc = Main->getLightControl(); // the function checkLight() sends a request to a hardware component, the response arrives asynchronously. // the use of Qt::BlockingQueuedConnection should be possible here, as the hardware response is delayed. connect(this, &Device::setLightEnabled, lc, &LightControl::checkLight); emit setLightEnabled(); m_mutex.lock(); Main->getWaitCondition()->wait(&m_mutex); m_mutex.unlock(); ... // in class Main, this function is called when the lightControl receives the response from the hardware. function wakeDevices() { m_waitCondition->wakeAll(); }
Everything seems to be fine on our test environment. But on other PCs the thread sometimes freezes.
Any ideas?Regards
Oliver -
Hi all,
I have a problem with a QWaitCondition and Signals sent right before the thread is waiting.
What can happen in the following situation? Is it possible that the signal is not sent immediately before the thread waits?
Do I have to use Qt::BlockingQueuedConnection to setup the signal?// in class Device: ... LightControl* lc = Main->getLightControl(); // the function checkLight() sends a request to a hardware component, the response arrives asynchronously. // the use of Qt::BlockingQueuedConnection should be possible here, as the hardware response is delayed. connect(this, &Device::setLightEnabled, lc, &LightControl::checkLight); emit setLightEnabled(); m_mutex.lock(); Main->getWaitCondition()->wait(&m_mutex); m_mutex.unlock(); ... // in class Main, this function is called when the lightControl receives the response from the hardware. function wakeDevices() { m_waitCondition->wakeAll(); }
Everything seems to be fine on our test environment. But on other PCs the thread sometimes freezes.
Any ideas?Regards
Oliver@stvokr
So far as I know, the signal will be emitted as soon as theemit setLightEnabled()
is executed (I don't think that has to wait until the thread's event loop is entered, but I stand to be corrected by an expert if that is not the case?). Since the connection is across threads that will queue the signal/slot to be called in the slot thread. And that will execute the next time that thread gets to process its event loop.Your caller does a
Main->getWaitCondition()->wait(&m_mutex);
and your receiver does am_waitCondition->wakeAll();
. You might assume that the caller will reach>wakeAll()
before the recipient reacheswait(&m_mutex)
, and this might the case "many times", but there is no guarantee they will not happen in the opposite order, depending on thread scheduling. If that happens thewakeAll()
will have executed before thewait()
. Test that case: doesn't that mean thewakeAll()
will have "wasted"/"lost" and then thewake()
will just hang/freeze?Qt::BlockingQueuedConnection
will doubtless handle this situation better than your code. But it still has its own issues. I don't know whether it will be preferable. Something like https://woboq.com/blog/how-qt-signals-slots-work-part3-queuedconnection.html gives some details on this.Another approach you might consider is not to block waiting for a signal to be delivered and a "response" (here
wakeAll()
) but instead have the receiver issue its own (non-blocking) signal when it has processed the original signal. And your thread puts a slot on that and does its "continuation" code there rather than in the original code right afteremit setLightEnabled();
. Depends what that code is needing to do. -
@stvokr
So far as I know, the signal will be emitted as soon as theemit setLightEnabled()
is executed (I don't think that has to wait until the thread's event loop is entered, but I stand to be corrected by an expert if that is not the case?). Since the connection is across threads that will queue the signal/slot to be called in the slot thread. And that will execute the next time that thread gets to process its event loop.Your caller does a
Main->getWaitCondition()->wait(&m_mutex);
and your receiver does am_waitCondition->wakeAll();
. You might assume that the caller will reach>wakeAll()
before the recipient reacheswait(&m_mutex)
, and this might the case "many times", but there is no guarantee they will not happen in the opposite order, depending on thread scheduling. If that happens thewakeAll()
will have executed before thewait()
. Test that case: doesn't that mean thewakeAll()
will have "wasted"/"lost" and then thewake()
will just hang/freeze?Qt::BlockingQueuedConnection
will doubtless handle this situation better than your code. But it still has its own issues. I don't know whether it will be preferable. Something like https://woboq.com/blog/how-qt-signals-slots-work-part3-queuedconnection.html gives some details on this.Another approach you might consider is not to block waiting for a signal to be delivered and a "response" (here
wakeAll()
) but instead have the receiver issue its own (non-blocking) signal when it has processed the original signal. And your thread puts a slot on that and does its "continuation" code there rather than in the original code right afteremit setLightEnabled();
. Depends what that code is needing to do.@JonB
Yes, I also found out that the wake() could be called earlier than wait(). That's why I'm thinking about the Qt::BlockingQueuedConnection.
And the other approach is not possible because the device thread is in a state machine that cannot be interrupted at this point.I will test the blocking connection.
Thanks. -
@JonB
Yes, I also found out that the wake() could be called earlier than wait(). That's why I'm thinking about the Qt::BlockingQueuedConnection.
And the other approach is not possible because the device thread is in a state machine that cannot be interrupted at this point.I will test the blocking connection.
Thanks.@stvokr said in QWaitCondition and Signals:
Yes, I also found out that the wake() could be called earlier than wait().
This is only guesswork/intuition, but maybe this is the correct order:
m_mutex.lock(); emit setLightEnabled(); Main->getWaitCondition()->wait(&m_mutex); m_mutex.unlock(); void wakeDevices() { m_mutex.lock(); m_waitCondition->wakeAll(); m_mutex.unlock(); }
[Your problem sorting out the sharing/visibility of
m_mutex
from secondary thread to main thread.]
My intention here, if it works, is to pre-lock the mutex before theemit
to make it so thewakeAll()
is not executed until thewait()
releases the mutex lock, so it cannot be "missed". -
@stvokr said in QWaitCondition and Signals:
Yes, I also found out that the wake() could be called earlier than wait().
This is only guesswork/intuition, but maybe this is the correct order:
m_mutex.lock(); emit setLightEnabled(); Main->getWaitCondition()->wait(&m_mutex); m_mutex.unlock(); void wakeDevices() { m_mutex.lock(); m_waitCondition->wakeAll(); m_mutex.unlock(); }
[Your problem sorting out the sharing/visibility of
m_mutex
from secondary thread to main thread.]
My intention here, if it works, is to pre-lock the mutex before theemit
to make it so thewakeAll()
is not executed until thewait()
releases the mutex lock, so it cannot be "missed".Hi,
From old memory, locking a mutex and emitting a signal afterward is a recipe for issues.
@stvokr can you explain why you would need a QWaitCondition ? The fact that your hardware sends an answer asynchronously sounds like it should be simpler to integrate with Qt. Send your request and when you get the answer, emit a signal at that point so you don't need any mutex or wait condition.
-
Hi,
From old memory, locking a mutex and emitting a signal afterward is a recipe for issues.
@stvokr can you explain why you would need a QWaitCondition ? The fact that your hardware sends an answer asynchronously sounds like it should be simpler to integrate with Qt. Send your request and when you get the answer, emit a signal at that point so you don't need any mutex or wait condition.
@SGaist Hi, this isn't so easy to explain.
I can even have several device threads that are to interact with the hardware, whereby only the first thread sends the request to the hardware.
The other requests are skipped. The hardware sends the response asynchronously, so I have to wait for it.
During this time, the other threads have to wait in the function they are currently in. They cannot leave the scope in between.