Condition variables are stateless. They do not have a "signaled" state or any state of their own.
To wait for something, you need state. You need to know whether the thing you're waiting for has happened (and thus you don't need to wait or can stop waiting) or not (in which case you need to wait). Without some state, there is no way you can decide whether or not to wait.
Again, the condition variable is stateless. So the state is not in the condition variable.
And the state needs to be shared. It needs to be accessed by the thread that is waiting for the thing to happen (to decide whether to wait and when to stop waiting) and the thread that indicates that the thing happened (so it can tell the other thread not to wait or to stop waiting). Thus, it must be synchronized somehow.
Now, you have a bit of a problem. Consider:
- You acquire the lock that protects the shared state.
- The thing you're waiting for hasn't happened yet.
- You wait.
Oops, the other thread can't change the shared state since you hold a lock on it. Let's try it again.
Maybe:
- You acquire the lock that protects the shared state.
- The thing you're waiting for hasn't happened yet.
- You release the lock.
- You wait.
Oops. What if after step 3 but before step 4, another thread acquires the lock, changes the shared state, signals the condition variable, and releases the lock. You're now waiting for something that already happened. (Remember, the condition variable is stateless and doesn't change to a "signaled" state.)
To fix this, a condition variable provides an atomic "unlock and wait" operation that does steps 3 and 4 atomically. This only works if the mutex associated with the condition variable protects the shared state.