Spurious wake-ups in Win32 condition variables


Win32 condition variables are subject to spurious wake-ups, where the sleeping thread wakes up but finds that the condition it is waiting for is not satisfied, so it has to go back to sleep. It woke up for no apparent reason.

One source of spurious wake-ups is the stolen wake-up, where a sleeping thread is woken, but by the time it gets a chance to run, another thread has already snuck in and taken the thing that the thread was waiting for, forcing the thread to go back and wait some more.

Another source of spurious wake-ups is where there are a lot of threads waiting on the condition variable, and then there is a huge flurry of Wake­Condition­Variable calls. Normally, exactly one thread is woken for each call to Wake­Condition­Variable, but if there are a lot of wakes in rapid succession, the internal data structure doesn't have enough room in the "number of threads that need to be woken" bitfield to record the exact number, and the system says, "Well, I'll play it safe and just wake up everybody."

The "number of threads that need to be woken" is a bitfield rather than a full 32-bit value because all of the bookkeeping for a condition variable must fit inside a pointer-sized variable, so you have to be very frugal with how you use that limited space. And since condition variables explicitly permit spurious wake-ups, it's okay to be sloppy in keeping track of how many wake-ups are required, as long as you always err on the side of waking up too many people.

Comments (16)
  1. Paul Topping says:

    Does this mean that the woken process should not assume the condition it was waiting on is satisfied? Should it check the condition explicitly and, if not satisfied, go back to waiting on the condition?

    1. voo says:

      Yes. And that’s not just limited to Win32’s condition variables, but the same applies to condition variables in POSIX and all the things built on top of those such as Java’s or .NET’s equivalents.

      Waiting outside of a loop is simply a bug.

    2. mZ says:

      Yes, exactly

    3. Yukkuri says:

      It’s exactly. This isn’t an uncommon pattern.

    4. DWalker07 says:

      Yes. :-)

    5. Adrian says:

      Yes, that’s exactly the case. Raymond also posted a few code snippets on exactly this matter a few weeks back.

    6. Antonio Rodríguez says:

      Yes. You should never assume that the condition is satisfied, and you should always check it after being awaken. The article’s last sentence explicitly says so: “And since condition variables explicitly permit spurious wake-ups, […]”.

    7. Dithermaster says:

      Yes.

    8. Tilmann Krueger says:

      Exactly.

    9. That is the skeleton of the standard process for waking up, yes:

      while (condition_unmet) { wait(); }

    10. Matthew Vincent says:

      It’s always safer to check the condition in a while loop to ensure that it’s satisfied before proceeding.

    11. Douglas Hill says:

      That’s what a spurious wakeup is.
      Quoth MSDN on condition variables:
      “Condition variables are subject to spurious wakeups (those not associated with an explicit wake) and stolen wakeups (another thread manages to run before the woken thread). Therefore, you should recheck a predicate (typically in a while loop) after a sleep operation returns.”

    12. Paul Topping says:

      Thanks for all the good responses. I suspected that was the case but thought it needed saying here. Perhaps it was too fundamental and didn’t need to be mentioned. Too late now. ;-)

      1. Ian Yates says:

        This blog has a regular reader community. It’s almost like a daily sermon from Raymond and he saves time, and presumably his sanity, by not repeating things he said a few posts back.
        He is better than most in giving references back to previous posts where, even though I know I’ve read them before, they’re often a worth a good read again anyway. Down the rabbit hole! :-)

  2. Alex Guteniev says:

    Switch to x64 to have fewer spurious wake-ups?

Comments are closed.

Skip to main content