Section 5.7 of “The Little Book of Semaphores” presents the last of the “less classical” synchronization problems. It’s another problem presented in “Concurrent Programming”, called “the roller coaster problem” and attributed to J.S. Herman. It reads as follows:

Suppose there are n passenger threads and a car thread. The passengers repeatedly wait to take rides in the car, which can hold C passengers, where C < n. The car can go around the tracks only when it is full.

Like in previous cases, the problem here is that multiple goroutines have to queue up waiting for some service and then they need to be notified about the completion of a process. One solution would be to have all the n goroutines send on a channel and have them block receiving from a second channel. On the other end, a goroutine would be reading from the first channel and when the process completes it would send n signals to the second channel. This would be enough to fulfill the constraints in the statement above.

I would like to explore another approach here. Have the n goroutines send to a channel, have the “car” goroutine receive C times from the same channel. What the sending goroutines would send is information about themselves (for illustration purposes the name suffices) and a channel that the “car” can use to notify the goroutines that the ride is over.

The passengers would look something like this:

	for {
		queue <- p
		<-p.Ch
	}

where p is the information about the passenger and p.Ch is the channel that the car uses to talk back to that specific goroutine.

The car would look something like this:

	passengers := make([]Passenger, 0, CarCapacity)

	// Load passengers
	for capacity := CarCapacity; capacity > 0; capacity-- {
		p := <-queue
		passengers = append(passengers, p)
	}

	// the car is full, go for a ride
	time.Sleep(100 * time.Millisecond)

	// Unload passengers
	for i := 0; i < CarCapacity; i++ {
		passengers[i].Ch <- struct{}{}
	}

And that’s it. This is enough to build a system like the one the problem describes.

One interesting thing to note is that with this solution, a goroutine getting off the car can get back on it right away, starving other goroutines that might have been waiting longer. One simple way to solve this issue is to make the channel corresponding to the queue buffered. In that way the passengers build up an actual queue. In that way, the goroutines getting off the car cannot immediately get back on it as there are other goroutines already in the front of the queue.

The full source is here.

Next week it’s the start of the not-so-classical problems, and it starts by manipulating linked lists.