2

I don’t understand why std::condition_variable has more standard guarantees than this code:

class condition_variable {
public:
  void notify_one() {}
  void notify_all() {}
  void wait(unique_lock<mutex> &lock) {
    lock.unlock();
    this_thread::sleep_for(15ms);
    lock.lock();
  }
  void wait(unique_lock<mutex> &lock,
            const function<bool()> &pred) {
    while (!pred()) wait(lock);
  }
};
JFMR
  • 23,265
  • 4
  • 52
  • 76
  • About the question of false signals triggers - on some systems it might be none (lest program termination is called or something similar) and only notification will reawaken the thread. But it might be larger on some other systems. – ALX23z Feb 18 '20 at 09:14
  • Related question: https://stackoverflow.com/questions/8594591/why-does-pthread-cond-wait-have-spurious-wakeups – Andrew Henle Feb 18 '20 at 10:47
  • Well, for starters, how did you happen to choose 15ms? We _hope_ that the people who implement `wait()` for any given operating system and hardware platform will do it in the most optimal way for that particular OS/hardware combination. The fact that `wait()` is allowed to "spuriously" return at all gives the implementors for _certain_ OS and hardware combinations some flexibility in how they make it work. – Solomon Slow Feb 18 '20 at 15:45

2 Answers2

3

The simplest implementation for std::condition_variable::wait() would just correspond to busy waiting:

template<typename Predicate>
void wait(std::unique_lock<std::mutex>& lck, Predicate pred) {
   while (!pred()) {
      lck.unlock();
      lck.lock();
   }
}

For this reason, spurious wakeups can occur.

Your implementation puts the thread to sleep between the release and the acquisition of the lock on the mutex:

void wait(unique_lock<mutex> &lock) {
   lock.unlock();
   this_thread::sleep_for(15ms); // <--
   lock.lock();
}

Finding the right sleeping period may be difficult, though. The lower it is, the more it resembles busy waiting, and therefore the more CPU cycles are wasted. The higher it is, the fewer CPU cycles are wasted, but the worse is the responsiveness.

How often spurious wakeups occur on your implementation depends on the election of the sleeping period.

JFMR
  • 23,265
  • 4
  • 52
  • 76
0

The condition variable is intended to use a semaphore, which allows waiting without using CPU cycles. Your implementation locks and unlocks mutex repeatedly, and without the sleep it would consume 100% of the CPU.

With the sleep there is a fixed delay when waking up, which is also a disadvantage compared to a semaphore. The operating system knows who are waiting for each condition variable, and therefore it is efficient to wake up.

On Windows, std::condition_variable would use SleepConditionVariableCS to wait, and generally utilize the operating system condition variable API.

VLL
  • 9,634
  • 1
  • 29
  • 54