[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#810868: threads: condition_variable or atomic causes _sometimes_ a deadlock



I have been able to locate the bug:

The implementation of the non-lambda version of condition_variable.wait()
in my compiler (g++ (Debian 4.9.2-10) 4.9.2) seems to be buggy;
I guess it is also in the other versions of g++.

One can simulate the buggy behaviour by enabling the 2 ifdef's below.
Then run the executable multiple times until the pgm hangs.

I tested it on a QuadCore-CPU ("AMD A10-5750M APU with Radeon(tm) HD Graphics").

For more info see initial posting.


// test.cpp
// g++ -Wall -O2 -std=gnu++11 test.cpp -lpthread
//
#include <iostream>
// #include <atomic>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <unistd.h>
#include <vector>

using namespace std;

mutex        mtx;
condition_variable cv;
bool fReady = false;
bool fQuit  = false;

void threadfunc(int id)
  {
/*
Any thread that intends to wait on condition_variable has to acquire a unique_lock<mutex> first. The wait operations atomically release the mutex and suspend the execution of the thread. When the condition variable is notified, the thread is awakened, and the mutex is reacquired [2].
*/

    while (!fQuit)
      {
        // wait for fReady
        unique_lock<mutex> lck(mtx);
#if 0
        // THIS IS BUGGY!
        cv.wait(lck);
#else
        // THIS IS OK
        cv.wait(lck, []{ return fReady || fQuit; });
#endif
        if (fQuit) break;
        if (!fReady) continue;

        // work, ie. process the job
        cout << "thread " << id << " has grabbed the job\n";
        //...

        // notify parent with fReady=false (ie. "job done")
        // wakes up also all the other threads
        fReady = false;
        cv.notify_all();
      }

    unique_lock<mutex> lck(mtx);
    cout << "thread " << id << " finished\n";
  }

int main()
  {
    const size_t N = 10;
    vector<thread> threads;
    for (size_t i = 0; i < N; ++i)
      threads.push_back(thread(threadfunc, i));

    for (size_t i = 0; i < 10000; ++i)
      {
        cout << i << " : " << N << " threads ready to race: ";

        // notify all threads about work to do
        {
        unique_lock<mutex> lck(mtx);
        fReady = true;
        }
        cv.notify_all();

#if 0
        // wait till job finished [THIS IS BUGGY!]
        {
        unique_lock<mutex> lck(mtx);
        while (fReady) cv.wait(lck);
        }
#else
        // wait till job finished [THIS IS OK]
        {
        unique_lock<mutex> lck(mtx);
        cv.wait(lck, []{ return !fReady; });
        }
#endif
      }

    // set fQuit and wake up all threads
    {
    unique_lock<mutex> lck(mtx);
    fQuit = true;
    }
    cv.notify_all();

    // wait till all threads have quit
    {
    unique_lock<mutex> lck(mtx);
    cout << "join\n";
    }
    for (auto& th : threads) th.join();

    cout << "pgm finished\n";
    return 0;
  }

// end of test.cpp


Reply to: