Thread communication with no QThread present
-
I'm working on a Qt application to control an industrial camera, and in particular I need to trigger the camera at a particular time (when various illumination settings have been put in place, for example), and wait until a frame is returned. When a frame is returned, a callback function gets called by the camera driver, from a separate thread which isn't a QThread.
In the simplest case, the following code does the job nicely:
@void AcquireFrame()
{
// Runs in the main GUI thread:
camera -> m_mutex.lock();
camera -> frameHasArrived = false;
camera -> m_mutex.unlock();
camera -> triggerImageAcquisition();forever { camera -> m_mutex.lock() bool isReady = camera -> frameHasArrived; camera -> m_mutex.unlock() if (isReady) { return; } else { Sleep(10); } }
}
void callback(camera *)
{
// Called by the camera driver from a separate OS thread -
// not a Qt thread - when a frame is ready:
camera -> m_mutex.lock();
camera -> frameHasArrived = true;
camera -> m_mutex.unlock();
}@...and most of the time this works perfectly well. However, this being the real world, occasionally the camera will fail to receive the trigger or the computer will fail to receive the frame cleanly, and the above code will then go into an infinite loop.
The obvious thing to do is to put in a timeout, so if the frame is not received within a certain time then the image acquisition can be attempted again. The revised code looks like:
@void AcquireFrame()
{
camera -> m_mutex.lock();
camera -> frameHasArrived = false;
camera -> m_mutex.unlock();
camera -> triggerImageAcquisition();QTime timeout; timeout.start(); forever { camera -> m_mutex.lock() bool isReady = camera -> frameHasArrived; camera -> m_mutex.unlock() if (isReady) { return; } else if (timeout.elapsed() > CAM_TIMEOUT) { // Assume the first trigger failed, so try again: camera -> triggerImageAcquisition(); timeout.restart(); } else { Sleep(10); } }
}@
Now, the problem is that with this latter version the failure rate (the proportion of 'unsuccessful triggers') is much, much higher - at least an order of magnitude. Moreover, this code too will eventually find itself in an infinite loop where, however many times it tries to re-trigger the camera, it never sees a frame come back. Under these latter circumstances, killing the application and checking the camera reveals that the camera is in perfect working order and patiently waiting for its next trigger, so it doesn't appear to be a camera problem. I'm coming to the conclusion that in fact it's some sort of a system resource issue or a thread conflict, so that Qt's event loop is not allowing the camera callback to be called at the proper time.
Is this likely, and is there in fact a better way of doing this? I have an idea that a QWaitCondition may be useful (so the GUI thread can wait until a frame has arrived, without going around in a loop), but I'm not sure whether this will work when the other thread is not a QThread.
-
Hi,
just as an idea. Might it be that you your sleep triggers in msecs? And that 10 msecs are not enough for the image?
In your first impolementation, you wait till the image arrives, which might be one or more iterations.
In your second one you retrigger each iteration, which might cause a failure if the 10 msecs are not enought -
Thank you, but that's not quite right. In the second example, the retrigger only happens if the elapsed time exceeds CAM_TIMEOUT, which I typically have set at somewhere between half a second and two seconds (I've been experimenting with different values, but it doesn't seem to make much difference to the likelihood of failure). If the timeout isn't exceeded, then control just loops back to the beginning after 10ms without sending the retrigger signal.
-
The weird thing (which makes me think that my underlying problem is with the thread control) is that the more diagnostics I add to try to understand what happens, the less reliable the system becomes. To answer your question, when things are working 'properly' it takes 20-30 ms between triggering the camera and receiving the frame. However, I've known frames to arrive 5-6 seconds after the trigger. Such long delays are much more likely in the second case than in the first.
-
For what it's worth, I've seen no more problems since I adopted the method below (having given the camera object an extra member, namely a QWaitCondition called 'm_condition'). It turns out that QWaitCondition will work perfectly well when called from another thread that isn't a QThread:
@void AcquireFrame()
{bool frameReceived; forever { camera -> triggerImageAcquisition(); camera -> m_mutex.lock(); frameReceived = camera -> m_condition.wait(&camera->m_mutex, CAM_TIMEOUT); if (frameReceived) { // We received a frame from the camera, so can return: camera -> m_mutex.unlock(); return; } // If we got to here, then the wait condition must have timed out. We need to // unlock the mutex, go back to the beginning of the 'forever' loop and try // again: camera -> m_mutex.unlock(); }
}
void callback (camera *)
{
// Called by the camera driver from a separate OS thread -
// not a QThread - when a frame is ready:
camera -> m_condition.wakeOne();
}@This still has the effect of pausing the main thread until we have either received a frame or experienced a timeout, but now we have eliminated the Sleep() and the Qt event loop remains in full control throughout. It's still not clear to me why the old method caused so many problems - I still suspect some sort of system resource limitation - but this new approach seems to be more lightweight and certainly works better.