Recently, I've been studying the classic Sleeping Barber problem and came across a possible solution from Wikipedia:
# The first two are mutexes (only 0 or 1 possible)
Semaphore barberReady = 0
Semaphore accessWRSeats = 1 # if 1, the number of seats in the waiting room can be incremented or decremented
Semaphore custReady = 0 # the number of customers currently in the waiting room, ready to be served
int numberOfFreeWRSeats = N # total number of seats in the waiting room
def Barber():
while true: # Run in an infinite loop.
wait(custReady) # Try to acquire a customer - if none is available, go to sleep.
wait(accessWRSeats) # Awake - try to get access to modify # of available seats, otherwise sleep.
numberOfFreeWRSeats += 1 # One waiting room chair becomes free.
signal(barberReady) # I am ready to cut.
signal(accessWRSeats) # Don't need the lock on the chairs anymore.
# (Cut hair here.)
def Customer():
while true: # Run in an infinite loop to simulate multiple customers.
wait(accessWRSeats) # Try to get access to the waiting room chairs.
if numberOfFreeWRSeats > 0: # If there are any free seats:
numberOfFreeWRSeats -= 1 # sit down in a chair
signal(custReady) # notify the barber, who's waiting until there is a customer
signal(accessWRSeats) # don't need to lock the chairs anymore
wait(barberReady) # wait until the barber is ready
# (Have hair cut here.)
else: # otherwise, there are no free seats; tough luck --
signal(accessWRSeats) # but don't forget to release the lock on the seats!
# (Leave without a haircut.)
At first this looked fine, but then I noticed a possible loophole:
From my limited understanding of semaphores, a signal()
can be executed before a corresponding wait()
.
Suppose the barber executes signal(barberReady)
and immediately starts cutting hair before a customer executes wait(barberReady)
. Conceptually this feels strange, because the customer isn't actually ready yet.
How does the operating system (or semaphore semantics) prevent this mismatch?
My understanding of the question
You’re worried about a “mismatch” if the barber does signal(barberReady) before a customer does wait(barberReady). Intuitively it feels wrong (“customer isn’t ready yet”), and you’re asking how the system avoids a lost or premature wake-up.
The primitives
custReady
(counting semaphore, init 0): how many customers are waiting.
barberReady
(counting semaphore, init 0): chair/ticket to start a haircut.
accessWRSeats
(mutex, init 1): protects numberOfFreeWRSeats
.
numberOfFreeWRSeats = N
: seats available in the waiting room.
Barber (one cycle)
wait(custReady)
— sleep until a customer exists.
wait(accessWRSeats)
→ numberOfFreeWRSeats += 1
→ signal(accessWRSeats)
— free one seat.
signal(barberReady)
— offer exactly one “start haircut” ticket.
Cut hair.
Customer (arrive once)
Take mutex: wait(accessWRSeats).
If a seat exists: decrement seat, signal(custReady), release mutex, then wait(barberReady) → get haircut.
Else: release mutex and leave.
Why “signal before wait” is OK
Counting semaphores store permits:
If signal(S) happens first, it increments S.
A later wait(S) just consumes that stored permit and returns immediately.
So there’s no lost wake-up; the barber’s early signal(barberReady) simply creates a ticket that the customer will take when they call wait(barberReady).