The author of “The Little Book of Semaphores” wrote this week’s problem inspired by real life events at Colby College. The problem’s statement is the following:

The following synchronization constraints apply to students and the Dean of Students:

Any number of students can be in a room at the same time.

The Dean of Students can only enter a room if there are no students in the room (to conduct a search) or if there are more than 50 students in the room (to break up the party).

While the Dean of Students is in the room, no additional students may enter, but students may leave.

The Dean of Students may not leave the room until all students have left.

There is only one Dean of Students, so you do not have to enforce exclusion among multiple deans.

Puzzle: write synchronization code for students and for the Dean of Students that enforces all of these constraints.

This problem is very similar to the previous one (the childcare center problem) in the sense that it’s about limiting access to a critical section to a particular thread only if thread of specific types are already present or not present.

A student can be represented by a goroutine like this:

```
go func() {
s := Student{Name: fmt.Sprintf("Student %d", i)}
r.sIn <- s
// ...
r.sOut <- s
}()
```

When the goroutine is *sending* to the `sIn`

channel, the student is
entering the room. After the send finishes, the student is *in* the
room. When it’s sending to the `sOut`

channel, it’s signaling that it
wants to leave the room, and after the send finishes, the student has
left the room.

The controlling logic is actually in the room itself:

```
func (r *Room) Run(timeout <-chan time.Time) {
var (
n int
dIn = r.dIn
dOut = r.dOut
sIn = r.sIn
)
```

The variables `dIn`

, `dOut`

and `sIn`

are used to control receiving from
those channels. When there are no students present, `dIn`

is not `nil`

to allow the dean to enter the room and conduct a search. During the
search, `sIn`

is `nil`

to prevent students from entering. If the dean
leaves, `sIn`

is not `nil`

to allow students to enter the room and `dIn`

is set to `nil`

to prevent the dean from entering. If enough students
enter the room, `dIn`

is set to a non-`nil`

value to allow the dean to
enter to break up the party. While students are leaving, `sIn`

is `nil`

and `dOut`

is also `nil`

to prevent the dean from leaving too early.

This is expressed in a `for-select`

loop:

```
for {
select {
case <-dIn:
// Dean entered, students cannot enter
sIn = nil
case <-dOut:
// Dean left, students can enter
sIn = r.sIn
case s := <-sIn:
n++
case s := <-r.sOut:
n--
case <-timeout:
return
}
```

In order to make sure that all the conditions are met at all times, the
check is performed after the `select`

, like this:

```
switch {
case n == 0:
// Dean can enter or leave
dIn = r.dIn
dOut = r.dOut
case n > r.Cap:
// Dean can enter
dIn = r.dIn
dOut = nil
default:
// Dean cannot enter
dIn = nil
dOut = nil
}
}
}
```

Conceptually, this solution is the same as the one for the previous
problem. It’s interesting to note that this is not true for the
semaphore-based solution in the book. The key thing to note in this and
prior solutions is that I have introduced “additional” threads to
represent the contended resource in one way or another. For example,
in this problem the *room* is a goroutine that does the bookkeeping.
It’s this “extra” goroutine that simplifies the solution. Instead of
having the dean and the students communicate by sharing memory, they
share memory by communicating.

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

The next problem in the book deals with boarding a bus.