Semaphores, Lock Implementation, Atomic Instructions
Semaphores
- Semaphores are a kind of generalized lock
- First defined by Dijkstra in late 60s
- Main synchronization primitive used in original UNIX
- Definition: a Semaphore has a non-negative integer value and supports the following operations:
- Set value when you initialize
Down()orP(): an atomic operation that waits for semaphore to become positive, then decrements it by1- Think of this as the
wait()operation
- Think of this as the
Up()orV(): an atomic operation that increments the semaphore by 1, waking up a waiting P, if any- This of this as the
signal()operation
- This of this as the
- Semaphores are like integers, except
- No negative values
- Only operations allowed are
PandV– can’t read or write value, except initially
- Operations must be atomic
- Two P’s together can’t decrement value below zero
- Thread going to sleep in
Pwon’t miss wake up fromV– even if both happen at same time
- POSIX adds ability to read value, but technically not part of proper interface!
Two Uses of Semaphores
- Mutual Exclusion (initial value = 1)
- Also called “Binary Semaphore” or “mutex”.
- Can be used for mutual exclusion, just like a lock:
semaP(&mysem); // Critical section goes here semaV(&mysem); - Scheduling Constraints(initial value = 0)
- Allow thread
1to wait for a signal from thread2- thread 2 waits for thread 1 to finish
// thread1 ThreadJoin { semaP(&mysem); //wait } // thread2 ThreadFinish { semaV(&mysem); // signal }
- Allow thread
Revisit Bounded Buffer: Correctness constraints for solution
- Correctness Constraints:
- Consumer must wait for producer to fill buffers, if none full (scheduling constraint)
- Producer must wait for consumer to empty buffers, if all full (scheduling constraint)
- Only one thread can manipulate buffer queue at a time (mutual exclusion)
- Remember why we need mutual exclusion
- Because computers are stupid
- Imagine if in real life: the delivery person is filling the machine and somebody comes up and tries to stick their money into the machine
- General rule of thumb: Use a separate semaphore for each constraint
- Semaphore fullBuffers; // consumer’s constraint
- Semaphore emptyBuffers;// producer’s constraint
- Semaphore mutex; // mutual exclusion

- Why asymmetry?
- Producer does:
semaP(&emptyBuffer),semaV(&fullBuffer) - Consumer does:
semaP(&fullBuffer),semaV(&emptyBuffer)
- Producer does: