5

Preface: I've seen similar questions here, but not one of them seems to answer my question.

Is there a reliable way to make sure that wait() method in consumer thread is called before the first notify_one() call from the producer thread?

Even with unique_lock in the consumer thread, there is a possibility that the producer thread will run first, lock the mutex and call notify() before the consumer calls wait(), therefore, my app will be missing first notify() call.

EDIT: Thanks for all your answers, they did help me. My problem was with first wait-notify() within this consumer loop:

while (!timeToQuit) {
    gdcv.wait(gdcondlock);
    gdlock.lock();
    //spurious wakeup
    if (gdQueue.empty()) {
      gdlock.unlock();
      continue;
    }
    //some work here
    gdlock.unlock();
} 

I guess I'll have to write extra code for the first loop iteration.

EDIT2: This loop and second lock(unique_lock btw) are there because there are multiple producers and consumers accessing queue.

EDIT3: The correct way for waiting on this particular thread with the help of boost::lockfree::queue, in case anyone has similar problem:

  nfq_data* data;
  while (!timeToQuit) {
    gdcv.wait(gdlock,[&]{return !gdQueue.empty() || timeToQuit;});
    gdQueue.pop(data);
    gdlock.unlock();
  }
Ajay
  • 18,086
  • 12
  • 59
  • 105
Vlad Fedyaev
  • 105
  • 1
  • 9
  • 1
    sounds almost like what you need is a semaphore rather than the condition variable - see here for example: http://stackoverflow.com/questions/4792449/c0x-has-no-semaphores-how-to-synchronize-threads/19659736#19659736 – Nim Nov 20 '15 at 10:50
  • Why are you calling `gdlock.lock()` and `gdlock.unlock()` instead of using `lock_guard` ? Do you dislike simplicity and correctness? – Jonathan Wakely Nov 20 '15 at 15:27
  • And why do you even have two locks? It sounds like you wouldn't even need to ask the question if the code was more idiomatic – Jonathan Wakely Nov 20 '15 at 15:33
  • 1
    Couldn't the entire thing be replaced with `lock_guard lg(m); gdcv.wait(lg, [&]{ return gdQueue.empty(); })`? This should do the right thing, i.e. return immediately if the condition is true or wait until notified and the condition is true. – screwnut Nov 20 '15 at 23:05
  • You should have `return !gdQueue.empty() || timeToQuit;` in your predicate's body. Otherwise, in order to quit you have to have things in your queue. i.e. What if the queue is empty but it's time to quit? – screwnut Nov 24 '15 at 19:54
  • 1
    You may have a very special situation but in the normal case, one doesn't need a lockfree container. The same mutex should be used when the container is modified, when notifying and when waiting. – screwnut Nov 24 '15 at 19:59

4 Answers4

6

Even with unique_lock in consumer thread there is a possibility that producer thread will run first, lock the mutex and call noify() before consumer calls wait(), therefore, my app will be missing first nofity() call.

Either the consumer has something to wait for, or it doesn't. If it has something to wait for, there is no problem. If it doesn't have anything to wait for, don't call wait. It really is that simple.

Call wait if, and only if, you want to wait.

Condition variables exist to solve the problem of how you can release a lock and wait without taking the risk that you will wait for something that has already happened. They solve it by providing a function that atomically releases the lock and waits. They cannot miss a wakeup because they hold the lock when they decide to sleep.

Ajay
  • 18,086
  • 12
  • 59
  • 105
David Schwartz
  • 179,497
  • 17
  • 214
  • 278
2

No, it's up to you to take care of threads synchronization.

If you don't want to miss a notify call even if it happens before the the consumer starts waiting, you must control this eventuality, recording somewhere that the producer is done, and then not call the wait() function at all.

For example, you can implement a sort of event class, that wait on the condition variable only if the event has not happened yet:

#include <mutex>
#include <condition_variable>

class Event
{
public:
    Event();
    void set_event();
    void reset_event();
    void wait_event();
private:
    std::mutex mtx;
    std::condition_variable cv;
    bool is_set;
};

Event::Event()
: is_set{false}
{}

void Event::set_event()
{
    std::lock_guard<std::mutex> lck{mtx};
    is_set = true;
    cv.notify_all();
}

void Event::reset_event()
{
    std::lock_guard<std::mutex> lck{mtx};
    is_set = false;
}

void Event::wait_event()
{
    std::unique_lock<std::mutex> lck{mtx};
    if( is_set )
        return;

    cv.wait(lck, [this]{ return is_set;} );
}
Paolo M
  • 12,403
  • 6
  • 52
  • 73
2

It sounds like you are trying to (mis)use condition_variable to implement a "barrier".

A condition variable allows you to wait for some condition to become true, tested via some predicate, e.g. "there is work available", and you should always test the predicate before waiting, which ensures you don't "miss" the event and wait when you should be working.

Using a condition variable purely to wait, without an associated predicate, does not work well. That's not how they are designed to be used.

If you are trying to make all threads wait at a particular point in the code and only proceed when they have all arrived then you are using a slightly different concept, known as a barrier.

The C++ Concurrency TS defines barriers (and the slightly simpler concept of "latches") for the C++ Standard Library, see the draft N4538.

You can define a barrier yourself by defining a class with a counter, which uses a condition_variable internally. The condition it needs to wait on is "all N threads have incremented the counter". Then you can make the producer and all consumers wait at the barrier, and they will all block until the last thread reaches the barrier. Even if the producer reaches the barrier and starts waiting first, you are guaranteed that the consumers will also stop and wait at the barrier, until all threads reach it, then they will all proceed.

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
1

Even with unique_lock in consumer thread there is a possibility that producer thread will run first, lock the mutex and call noify() before consumer calls wait(), therefore, my app will be missing first nofity() call

If the producer has already run, then the consumer doesn't have to wait and therefore shouldn't call wait. If the consumer only waits when it needs to - and the condition is snychronized - then it cannot miss a notify that it needs to notice.

eerorika
  • 232,697
  • 12
  • 197
  • 326