There is a 2-thread application; GUI thread adds tasks, worker thread processes them. However, sometimes Q_ASSERT detects false task.
So the question: is it possible, that
- Thread 1 locks the mutex, sets _hasTask to true, calls wakeAll, and unlocks the mutex,
- _hasTaskCondition memorizes that it should wake up, but does not have time to lock the mutex, because
- Thread 1 again calls addTask with false argument, locks the mutex, sets _hasTask to false, don't call wakeAll now, unlocks the mutex.
- So now Thread 2 have _hasTask == false, but the thread is woken up, locks the mutex and processes the task => boom.
Some debugging (MSVC 2013) shows that it is can happen. If Qt does not allow such scenario, then what is probably going on here?
Simplified code to illustrate the logic:
Class Worker, member variables:
unsigned int _taskData; // init with 0
QMutex _mutex;
QWaitCondition _hasTaskCondition;
Thread 1 (GUI QThread):
// Called from time to time, sometimes very often
// input > 0 - to wake worker thread to process some work,
// input == 0 - to put worker thread to sleep on next iteration
void Worker::addTask(unsigned int taskData)
{
QMutexLocker lock(&_mutex);
if (taskData == _taskData)
return;
_taskData = taskData;
if (_taskData > 0)
_hasTaskCondition.wakeAll();
}
Thread 2 (worker QThread):
// In a loop state
void Worker::doWork()
{
unsigned int currentTaskData = 0;
while (true)
{
_mutex.lock();
if (_taskData == 0)
_hasTaskCondition.wait(&_mutex);
Q_ASSERT(_taskData > 0); // error at some moment
// copy task to process it without locking the mutex
currentTaskData = _taskData;
_taskData = 0;
_mutex.unlock();
// Process currentTaskData:
// some useful work without touching
// _hasTask, _mutex and _hasTaskCondition variables
}
}