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: