Home > Net >  How could tell which way is condition_variable.wait_for unblocked by, spurious wakeup or cv_status::
How could tell which way is condition_variable.wait_for unblocked by, spurious wakeup or cv_status::

Time:06-15

As far as I know, only condition_variable.wait_for with predicate(because double check inside) could avoid to be unblocked by spurious wakeup, but not the version without predicate(use if not while).

But what if I want to do something when only cv_status::timeout happened and do something else by notify_XXX? because condition_variable.wait_for with predicate returns only bool, it cannot tell if it is unblocked by notify_XXX or cv_status::timeout; and although condition_variable.wait_for without predicate returns cv_status::timeout, but it cannot tell if it is unblocked by spurious wakeup or notify_XXX.

CodePudding user response:

Condition variables are best used as a triple. The cv, the mutex, and the payload.

Without the payload (implicit or explicit), there is no way to determine if the wakeup is spurious or not.

The predicate version makes it easy to check the payload, but in some complex situations checking the payload might be easier without doing it in a lambda. So the other API is provided.

After modifying the payload, the mutex that the condition variable operates on must be in a locked state afterwards before you send signal. (You could guard the payload with the mutex, for example; or you could modify the payload, atomically, then lock and unlock the mutex, then send the signal). Otherwise, the opposite of spurious wakeup (a missed signal) can occur.

All of this is tricky to get right, and easy to accidentally get wrong.

If you want to write new concurrency code (especially using low level primitives), you have to learn enough of the C memory model and learn how to prove your algorithms are correct. Because it is way, way to hard to write code and base its correctness based on "does it work".

You have correctly identified that you can't solve this without additional data. You need to add that additional data, and use it to determine if the wakeup was spurious or real. That is by design.

C could have added that additional data to condition variable, but then it would have made you pay for it even if you aren't using it. Condition variable is a low level primitive that lets you write code as near to optimal as possible, the fact is it wrapped in a class can be confusing to some people.

And there are lots of payloads. If you have a counting semaphore, where the number of sent signals matches the number of received signals, your payload is going to be an integer. If you have a latch or gate, where once open everyone is free to go through it, your payload is going to be a bool.

struct gate {
  void wait_on_gate() const {
    auto l = lock(); 
    cv.wait( l, [&]{ return !closed; } );
  }
  // false iff it times out
  template<class Time>
  bool wait_on_gate_until(Time time) const {
    auto l = lock(); 
    return cv.wait_until( l, time, [&]{ return !closed; } );
  }
  // false iff it times out
  template<class Duration>
  bool wait_on_gate_for(Duration d) const {
    auto l = lock(); 
    return cv.wait_for( l, d, [&]{ return !closed; } );
  }
  // Once you call this, nobody waits
  void open_gate() {
    auto l = lock();
    closed = false;
    cv.notify_all();
  }
private:
  mutable std::mutex m;
  std::condition_variable cv;
  bool closed = true;
};

now you'll note I'm using the lambda version.

We can refactor to the non-lambda version:

  void wait_on_gate() const {
    auto l = lock(); 
    while(closed)
      cv.wait( l );
  }
  template<class Time>
  void wait_on_gate_until(Time time) const {
    auto l = lock(); 
    while(closed) {
      if (cv.wait_until(l, time) == std::cv_status::timeout)
        return !closed;
    }
    return true;
  }

which is more complex, and acts exactly the same. (assuming I have no typos).

The only difference is you could do fancy things that might not fit in a lambda. For example, you could choose to say "well, it was spurious, but while I'm awake I'll go do some bookkeeping somewhere else and come back later".

  • Related