I am getting a deadlock I can't explain with the below code. I expected that the code should work because of the spurious wake-up but I think I am missing something. I checked this Condition variable deadlock but no luck.
class SafeQueue
{
private:
std::queue<int> data_queue;
mutable std::mutex m;
std::condition_variable cv;
std::atomic<bool> flag{ true }; // Atomic flag to control the queue
public:
void push(int val)
{
std::lock_guard<std::mutex> lock(m);
data_queue.push(val);
cv.notify_one(); // Notify waiting thread upon pushing data
}
bool pop(int& val)
{
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, [this]() { return !data_queue.empty() || !flag; }); // Wait until queue is not empty or flag is turned off
if (!flag && data_queue.empty())
{
return false; // Queue is empty and flag is off, return false to indicate termination
}
if (!data_queue.empty())
{
val = data_queue.front();
data_queue.pop();
return true;
}
return false;
}
void turnOff()
{
flag = false;
}
bool isFlagOn() const
{
return flag;
}
};
void consumerLoop(SafeQueue& q)
{
while (q.isFlagOn())
{
int val;
if (q.pop(val))
{
std::cout << "Consumed: " << val << std::endl;
}
}
std::cout << "Consumer exiting" << std::endl;
}
int main()
{
SafeQueue q;
std::thread consumerThread(consumerLoop, std::ref(q));
// Producer pushes some values into the queue
for (int i = 0; i < 10; ++i)
{
q.push(i);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
// Turn off the queue flag
q.turnOff();
consumerThread.join();
return 0;
}
Can you please help point out where it happens and how to correct the implementation? Any other suggestions for implementation are welcome (I need a deque I just included the example for queue for simplicity)
Your consumerThread
is hanging on cv.wait
and only waking up when push
does a cv.notify_one()
. This happens 10 times, then the producer loop is done and flag
is turned off - however consumerThread
is still/again hanging on cv.wait
.
At this point your consumerThread
is stuck until either:
cv.notify_one()
(or cv.notify_all()
)A fairly simple solution in this case is to do this:
void turnOff()
{
flag = false;
cv.notify_one();
}