One use of condition variables that's a bit more complicated than just a message queue, is to "share a lock", where different threads are waiting for subtly different conditions of the same basic nature. For instance, you have a (very shonky, simplified) web cache. Each entry in the cache has three possible states: not present, IN_PROGRESS, COMPLETE.
getURL:
lock the cache
three cases for the key:
not present:
add it (IN_PROGRESS)
release the lock
fetch the URL
take the lock
update to COMPLETE and store the data
broadcast the condition variable
goto COMPLETE
COMPLETE:
release the lock and return the data
IN_PROGRESS:
while (still IN_PROGRESS):
wait on the condition variable
goto COMPLETE
I have in practice used the pattern to implement a variant of the POSIX function pthread_once
without any help from the scheduler. The reason I couldn't use a semaphore or lock per once_control
, and just do the initialization under the lock, is that the function wasn't allowed to fail, and the once_control
had only trivial initialization. For that matter, pthread_once
itself has no defined error codes, so implementing it to possibly fail doesn't leave your caller with any good options...
Of course with this pattern you have to be careful about scaling. Each time any initialization is completed, every waiting thread wakes up to grab the lock. So when you design the system you think very carefully about sharding, and then decide you can't be bothered doing anything to actually implement it until you see proven performance problems.