## Condition variables (“cond var” in these notes)

• What they are
• Let’s say one thread wants to wait for another thread to finish something that takes a long time
• To do this with mutexes, thread would have to spin-wait
• Sit in a loop, locking the mutex, checking the shared data, unlocking mutex…
• See 23_spinwait.c
• Very inefficient!
• If we were to remove the sleep calls, those threads would spin and use all the CPU
• Condition variables are a way to solve this.
• When you lock an already-locked mutex, the thread sleeps until the mutex is unlocked.
• Condition variables are a little more general
• They let a thread sleep until some other thread says it’s time to wake up.
• Cond vars give you two operations:
• wait on a cond var, to sleep until the condition is met
• signal a cond var, to wake up a thread waiting on it.
• there’s some weirdness here…
• Like with mutexes and global variables…
• The cond var does not contain the condition itself
• It’s up to you to check the ACTUAL condition (global variable?)
• And it’s up to you to only wake up other threads when an ACTUAL condition is met.
• Cond vars are paired with a mutex!
• The cond var is itself a shared object and access must be synchronized
• But they work with the mutex in an interesting way…
• How they work
• See 23_condvar.c
• Create a mutex and associated cond var.
• To wait on a cond var:
1. Lock the mutex
2. Wait on the cond var in association with the mutex
• pthread_cond_wait(&condvar, &mutex);
3. When the wait finishes, the condition has been signaled
• IMPORTANT: due to complex implementation details, the condition might not have been signaled
• lol
• so you have to re-check the actual condition in a loop until it’s true.
4. Finally, unlock the mutex
• To signal a cond var:
1. Lock the mutex
2. Change any shared state, if needed
3. Signal/broadcast on the cond var
• pthread_signal(&condvar);
4. Unlock the mutex
• signal means “wake up at least one waiting thread”
• for the reason above.
• only one thread will actually “win” though.
• Why the mutex?
• Well, wait has an interesting behavior
• It atomically unlocks the mutex and blocks the thread.
• It does this to avoid race condition between signal and wait
• Otherwise signal could be sent to a thread that sleeps after the signal is sent.. and it will never wake up!
• Before a thread can wake up when signaled, it reacquires the mutex
• So now you have to unlock it as usual
• From your perspective it looks like the thread just keeps the mutex forever
• But it don’t.

## Applications of condition variables

• Producer-consumer
• See 23_prodcons.c
• Very common!
• 1 or more “producers” (something that makes “things to do”)
• 1 or more “consumers” (something that “does the things”)
• Producers put the things to do into a list/queue/whatever (a “todo” box)
• Consumers take things out and handle them
• Consider a web server:
• It sits and waits for someone to connect to it.
• When someone does, it produces a task: “respond to that client.”
• A consumer thread says “ok, I’ll handle it” and responds to the client.
• Typically there are lots of clients coming in at once, so we have lots of consumer (“worker”) threads.
• Barrier synchronization
• You more or less did this in the last lab, but…
• In the lab, you waited for every thread to exit
• With condition variables, you can wait for every thread to get to a certain checkpoint
• And then let them continue on once everyone gets there.

## Semaphores?

• They’re kinda like a generalized version of a mutex
• With a mutex, either 0 or 1 threads are allowed to use something.
• With a semaphore, 0 through n threads are allowed to use something. (Or a collection of things.)
• Consider a motel or hotel
• Fixed number of rooms
• Only one guest can check in/out at a time
• If all rooms are taken, they turn on their “no vacancy” sign and no one else can come in
• It starts off with a numerical value n - let’s say 10 here
• Every time a thread locks it:
• If n == 0, the thread sleeps (just like with a mutex).
• Otherwise, n is decremented.
• Every time a thread unlocks it:
• n is incremented.
• If n was 0, we can now unblock any threads waiting on it.
• You could do this with a mutex/condition variable, but…
• Sometimes it’s just more natural to use a semaphore.
• Just like it’s sometimes more natural to use a cond var instead of a spin-wait.

• Traffic example
• Two cars come to an intersection on opposing sides
• They both want to turn left
• They pull into the intersection and try to turn left…
• …but each blocks the other.
• Circular wait
• two (or more…) threads are both waiting on resources the other has.
• Mutual exclusion
• only one thread can use the resource at once.
• Hold-and-wait
• a thread doesn’t give up any resources it already holds while attempting to acquire a new one.
• it’s “greedy.”
• No preemption of the resource
• no one can forcibly take the resource from the thread that has locked it.
• If you make any one of those conditions false, a deadlock will end.
• If the two threads don’t share a resource, they can’t deadlock (duh).
• If the two threads can both share the resource, they can’t deadlock.
• If the two threads let go of one resource before trying to grab another, they can’t deadlock.
• If the resource is forcibly taken from one of the threads, the deadlock ends.
• Unfortunately…
4. Use pthread_cond_timedwait when possible