The next problem in the “The Little Book of Semaphores” is called the “unisex bathroom problem”, by Allen Downey himself. The problem statement reads as follows:

A friend of the author took a job at Xerox. She was working in a cubicle in the basement of a concrete monolith, and the nearest women’s bathroom was two floors up. She proposed to the Uberboss that they convert the men’s bathroom on her floor to a unisex bathroom, sort of like on Ally McBeal.

The Uberboss agreed, provided that the following synchronization constraints can be maintained:

• There cannot be men and women in the bathroom at the same time.

• There should never be more than three employees squandering company time in the bathroom.

It’s a categorical exclusion problem: the limited resource is the bathroom and the exclusion is that any number (up to three) of females or any number of males can be in the bathroom simultaneously, but never a mix of them. Unlike the readers-writers probelm, if a category of workers is waiting for the shared resource, this does not limit the access to the resource by the other category. For example, if the bathroom is currently occupied by males, other males can come in, but a female arriving at the queue does not prevent more males from going in if there’s enough space. Obviously this leads to starvation.

This problem cannot be solved by simply repurposing sync.RWMutex. Let’s sale that “readers” represent females and “writers” represent males. Any number of females can use the bathroom at the same time, but only one male would be able to do so. The problem can be solved by using a sync.Cond, but the solution is a little convoluted.

A simple solution can be developed using channels, with a pattern that has been used before in this series. Start by declaring a Bathroom as a structure holding some channels:

type Bathroom struct {
	m       chan Person
	f       chan Person
	exit    chan Person
	males   int
	females int
}

The m and f channels are used by males and females to queue at the bathroom. The males and famales fields are counters to keep track of occupancy. The exit channel is used by a Person to signal that it’s leaving the bathroom. To use the bathroom, a Person does this:

func (p Person) UseBathroom(in, out chan Person) {
	in <- p
	time.Sleep(time.Duration(250+rand.Intn(100)) * time.Millisecond)
	out <- p
}

the person queues at the bathroom door, stays a little in there and then leaves the bathroom. The Bathroom itself enforces the constraints:

func (b *Bathroom) Service() {
	m := b.m
	f := b.f
	for {
		select {
		case <-m:
			b.males++
			f = nil
			if b.males == 3 {
				m = nil
			}
		case <-f:
			b.females++
			m = nil
			if b.females == 3 {
				f = nil
			}
		case p := <-b.exit:
			switch p.Gender {
			case Male:
				b.males--
				m = b.m
				if b.males == 0 {
					f = b.f
				}
			case Female:
				b.females--
				f = b.f
				if b.females == 0 {
					m = b.m
				}
			}
		}
	}
}

The two local variables m and f are used to control who can enter. When a male enters the bathroom, f is set to nil. When a female enters, m is set to nil. This based on Dave Cheney’s Channel Axioms, specifically “a receive from a nil channel blocks forever”. In this way, when a male is in the bathroom, other males can enter (because that channel is not nil) but females cannot. If a male enters the bathroom and the occupancy reaches 3 people, the m channel is set to nil to prevent more males from entering. The same logic applies for females.

When a male leaves the bathroom by sending the signal on the exit channel, the occupancy is decreased and the channel for entering the bathroom is restored. If the bathroom becomes empty, then the other channel is “activated” again. In this way the constraints are satisfied.

Note that while people exiting the bathroom require only one channel, people entering the bathroom require two because it’s necessary to stop a category from entering the bathroom when the other is already present. When exiting, the category doesn’t matter. Also note that the males and females counters are not necessary, it’s enough to have an occupancy counter, but I kept the two counters for clarity in the presentation.

I find this solution simpler than the equivalent one based on semaphores if for no other reason that it’s simple to follow.

The full source (with minor modifications for presentation) is here.

The next problem in the book is called the “baboon crossing problem” and it’s conceptually identical to this one. After that comes the “Modus Hall problem”, which is very similar, but contains an interesting variation to the constraints.