Printing to stdout causes blocked goroutine to run?

Issue

As a silly basic threading exercise, I’ve been trying to implement the sleeping barber problem in golang. With channels this should be quite easy, but I’ve run into a heisenbug. That is, when I try to diagnose it, the problem disappears!

Consider the following. The main() function pushes integers (or “customers”) onto the shop channel. barber() reads the shop channel to cut “customers'” hair. If I insert a fmt.Print statement into the customer() function, the program runs as expected. Otherwise, barber() never cuts anyone’s hair.

package main

import "fmt"

func customer(id int, shop chan<- int) {
    // Enter shop if seats available, otherwise leave
    // fmt.Println("Uncomment this line and the program works")
    if len(shop) < cap(shop) {
        shop <- id
    }
}

func barber(shop <-chan int) {
    // Cut hair of anyone who enters the shop
    for {
        fmt.Println("Barber cuts hair of customer", <-shop)
    }
}

func main() {
    shop := make(chan int, 5) // five seats available
    go barber(shop)
    for i := 0; ; i++ {
        customer(i, shop)
    }
}

Any idea what’s afoot?

Solution

The problem is the way Go’s scheduler is implemented. The current goroutine can yield to other goroutines only when it makes a system call or a blocking channel operation. fmt.Println makes a system call, giving the goroutine an opportunity to yield. Otherwise it doesn’t have one.

In practice this doesn’t often matter, but for small problems like this it can sometimes crop up.

Also, a more idiomatic, less racy way of doing a non-blocking send on a channel is:

func customer(id int, shop chan<- int) {
    // Enter shop if seats available, otherwise leave
    select {
    case shop <- id:
    default:
    }
}

The way you’re doing it, a customer could end up waiting outside of the barber shop since by the time you actually do the send, len(shop) may have changed.

Answered By – Evan Shaw

Answer Checked By – Pedro (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.