tesla1060
tesla1060

Reputation: 2765

c++ 11 condition_variable wait spurious wake up is not working

I tried to write a simple producer/consumer by using condition_variable,

include <iostream>
#include <thread>
#include <condition_variable>
#include <mutex>
#include <chrono>
#include <queue>
#include <chrono>
using namespace std;

condition_variable cond_var;
mutex m;
int main()
{
    int c = 0;
    bool done = false;
    cout << boolalpha;
    queue<int> goods;

    thread producer([&](){
        for (int i = 0; i < 10; ++i) {
            m.lock();
            goods.push(i);
            c++;
            cout << "produce " << i << endl;
            m.unlock();
            cond_var.notify_one();
            this_thread::sleep_for(chrono::milliseconds(100));
        }
        done = true;
        cout << "producer done." << endl;
        cond_var.notify_one();
    });

    thread consumer([&](){
        unique_lock<mutex> lock(m);
        while(!done || !goods.empty()){
            /*
            cond_var.wait(lock, [&goods, &done](){
                        cout << "spurious wake check" << done <<endl;
                        return (!goods.empty() || done);
            });
            */  
            while(goods.empty())
            {
                cout<< "consumer wait" <<endl;
                cout<< "consumer owns lock " << lock.owns_lock() <<endl;
                cond_var.wait(lock);
            }
            if (!goods.empty()){
                cout << "consume " << goods.front()<<endl;
                goods.pop();
                c--;
            }
        }
    });

    producer.join();
    consumer.join();
    cout << "Net: " << c << endl;
}

The problem I have now is when the consumer consumes the last item before the producer set done to true, the consumer thread will stuck in

 while(goods.empty())
            {
                cout<< "consumer wait" <<endl;
                cout<< "consumer owns lock " << lock.owns_lock() <<endl;
                cond_var.wait(lock);
            }

My understanding is cond_var.wait(lock) will wake up spuriously and thus exit the while(good.empty()) loop, but it seems not the case?

Upvotes: 3

Views: 4528

Answers (3)

ktime
ktime

Reputation: 63

If the Producer notifies {cond_var.notify_one();} without any consumer waiting {cond_var.wait(lock);} then the 1'st notification that is sent to the consumer has gone unnoticed.

@tesla1060 "The problem I have now is when the consumer consumes the last item before the producer set done to true, the consumer thread will stuck in" , this is not ture. The fact is that the Consumer has not received any notification from the Producer (as it has missed one notification (the 1'st one)).

Upvotes: 0

G. Sliepen
G. Sliepen

Reputation: 7973

As @Karlinde says, and as the name implies, spurious wakeups are not guaranteed to happen. Rather, they will normally not happen at all.

But, even if spurious wakeups would happen, that would not fix your issue: you simply have an infinite loop in your program. Once the producer has stopped, goods.empty() is true and it will never change again. So change the while loop to:

while(!done && goods.empty())
{
    ...
}

Now it should exit... most of the time. You still have a possible race condition, because in the producer, you set done = true without holding the lock.

Upvotes: 0

Karlinde
Karlinde

Reputation: 136

Spurious wakeups are not a regular occurance which you can rely on to break a loop in the manner you're suggesting. The risk of having a spurious wakeup is an unfortunate side-effect of the current implementations of condition variables which you must account for, but there is no guarantee about when (if ever) you will experience a spurious wakeup.

If you want to ensure that the consumer thread doesn't get stuck waiting for a notify that never comes, you might try using std::condition_variable::wait_for() instead. It takes a duration and will timeout and reaquire the lock if the duration expires. It might be viewed as closer to a busy wait but if the timeout is long enough the implications on performance should be negligible.

Upvotes: 3

Related Questions