Search code examples
c++multithreadingcondition-variable

Testing multiple threads waiting on condition_variables in a queue


I am testing how to push objects waiting on condition_variables in a queue. I want to execute the threads as per my wish because they will be in critical sections later. Nothing is printed from the threads, what could be wrong ?

mutex print_mu;

void print(function<void()> func)
{
    lock_guard<mutex> lock(print_mu);
    func();
}

unsigned int generate_id()
{
    static unsigned int id = 1;
    return id++;
}

class foo
{
    unsigned int id_;
    mutex mu_;
    condition_variable cv_;
    bool signal_;
    bool& kill_;
public:
    foo(bool kill) 
        :kill_(kill) 
        , signal_(false)
        , id_(generate_id())
    {
        run();
    }

    void set()
    {
        signal_ = true;
    }

    void run()
    {
        async(launch::async, [=]()
        {
            unique_lock<mutex> lock(mu_);
            cv_.wait(lock, [&]() { return signal_ || kill_ ; });

            if (kill_)
            {
                print([=](){ cout << " Thread " << id_ << " killed!" << endl; });
                return;
            }

            print([=](){ cout << " Hello from thread " << id_ << endl; });
        });
    }
};

int main()
{
    queue<shared_ptr<foo>> foos;
    bool kill = false;

    for (int i = 1; i <= 10; i++)
    {
        shared_ptr<foo> p = make_shared<foo>(kill);
        foos.push(p);
    }
    this_thread::sleep_for(chrono::seconds(2));

    auto p1 = foos.front();
    p1->set();
    foos.pop();

    auto p2 = foos.front();
    p2->set();
    foos.pop();

    this_thread::sleep_for(chrono::seconds(2));

    kill = true; // terminate all waiting threads unconditionally

    this_thread::sleep_for(chrono::seconds(2));

    print([=](){ cout << " Main thread exits" << endl; });

    return 0;
}

Solution

  • When a thread calls std::condition_variable::wait, it will block until another thread calls notify_one or notify_all on the same condition_variable. Since you never call notify_* on any of your condition_variables they will block forever.

    Your foo::run method will also block forever, since std::future's destructor will block waiting for the result of a std::async call if it's the last std::future referencing that result. Thus your code deadlocks: your main thread is blocked waiting for your async future to finish, and your async future is blocked waiting for your main thread to signal cv_.

    (Also foo::kill_ is a dangling reference. Well, it would become one if run ever returned anyway.)