Go

Does an unbuffered channel give a happens-before guarantee? What does the memory model say about channels?

Question 211HardGo 1.22 to 1.25

Yes. The Go memory model defines these synchronization rules for channels:

  1. A send on a channel is synchronized before the corresponding receive completes.
  2. Closing a channel is synchronized before a receive that returns because the channel is closed.
  3. For an unbuffered channel, a receive is synchronized before the corresponding send completes.
  4. For a buffered channel with capacity C, the k-th receive is synchronized before the (k+C)-th send completes. This is what makes a buffered channel a valid counting semaphore.
var data string
done := make(chan struct{})

go func() {
	data = "hello"   // (a)
	close(done)      // (b)  a before b (program order)
}()

<-done               // (c)  b synchronized before c
fmt.Println(data)    // guaranteed "hello", no data race

Rule 3 is subtle: with an unbuffered channel, the sender also learns that the receiver has reached the receive point. Swapping in a buffered channel of size 1 breaks any code that relies on that. Interviewers at the senior level like to ask "if I change this to make(chan struct{}, 1), is it still correct?" The answer depends on which direction the synchronization flows.

More on Channels & select

All 38 Channels & select questions