In section 5.6 of “The Little Book of Semaphores”, Allen Downey says this problem might have originally come from Anthony Joseph at U.C. Berkeley:

Somewhere near Redmond, Washington there is a rowboat that is used by both Linux hackers and Microsoft employees (serfs) to cross a river. The ferry can hold exactly four people; it won’t leave the shore with more or fewer. To guarantee the safety of the passengers, it is not permissible to put one hacker in the boat with three serfs, or to put one serf with three hackers. Any other combination is safe.

This is a variation on the H₂O problem from last week. Threads have to wait for other threads to arrive at the rendezvous, with the caveat that different threads are allowed pass depending on the number that have already passed.

The “problematic” situation is when the total number of hackers and serfs that have already boarded the boat is three:

  • If two hackers and one serf have boarded, only serfs can board.
  • If there are three serfs, only serfs can board.
  • If there are three hackers, only hackers can board.
  • If there is one hacker and two serfs, only hackers can board.
  • In any other case, anyone can board

With this in mind, the solution is simple: have a goroutine producing hackers and another producing serfs, with each of them sending hackers or serfs on their respective channel. With this setup, another gorutine can read from both channels using select and keep tabs on how many of each type have boarded the boat. Once the total number is three, perform the checks above and stop reading on the appropriate channel.

Like this:

	select {
	case <-hackerCh:
		hackers++
	case <-serfCh:
		serfs++
	}

	if hackers+serfs == 3 {
		switch {
		case hackers == 2 || serfs == 3:
			hackerCh = nil
		case hackers == 3 || serfs == 2:
			serfCh = nil
		}
	}

Just like in H₂O, this is based on Dave Cheney’s channel axioms, specifically “a receive from a nil channel blocks forever”.

This might look like an oversimplification of the original problem, but note that the core of the original problem is letting the correct threads advance. Even if I have illustrated the solution using goroutines producing either hackers or serfs, the same solution can be extended to many goroutines corresponding to one hacker or one serf, sending on the corresponding channel and waiting for a signal that they can proceed.

The full source is here.

Next week we are going to the amusement park!