0

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

  1. Thread 1 locks the mutex, sets _hasTask to true, calls wakeAll, and unlocks the mutex,
  2. _hasTaskCondition memorizes that it should wake up, but does not have time to lock the mutex, because
  3. Thread 1 again calls addTask with false argument, locks the mutex, sets _hasTask to false, don't call wakeAll now, unlocks the mutex.
  4. 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
    }
}
trig-ger
  • 1,195
  • 1
  • 11
  • 18

1 Answers1

0

change the if to a while:

while(_taskData == 0)
    _hasTaskCondition.wait(&_mutex);

Then when wakeAll happens the thread will recheck if it actually has a task and then continue waiting

ratchet freak
  • 47,288
  • 5
  • 68
  • 106
  • Like here, with pthreads: http://stackoverflow.com/questions/4544234/calling-pthread-cond-signal-without-locking-mutex – trig-ger Oct 22 '14 at 16:45
  • But there is no such loop in Qt example: http://qt-project.org/doc/qt-4.8/qwaitcondition.html – trig-ger Oct 22 '14 at 17:10
  • it's just how the idiom for wait variables is, you lock and start checking the checking your variable in a loop and wait on the condition in the body of the loop. – ratchet freak Oct 22 '14 at 17:45