Why might a program crash on incorrect synchronization?

The following excerpt is from “The Go Memory Model” (at The Go Memory Model - The Go Programming Language):

A receive from an unbuffered channel happens before the send on that channel completes.

This program (as above, but with the send and receive statements swapped and using an unbuffered channel):

var c = make(chan int)
var a string

func f() {
	a = "hello, world"
	<-c
}

func main() {
	go f()
	c <- 0
	print(a)
}

is also guaranteed to print “hello, world”. The write to a happens before the receive on c, which happens before the corresponding send on c completes, which happens before the print.

If the channel were buffered (e.g., c = make(chan int, 1)) then the program would not be guaranteed to print “hello, world”. (It might print the empty string, crash, or do something else.)

The last (parenthetical) sentence indicates that the program might crash or “do something else”. However, why would this be? I see only two possibilities:

  • The goroutine “f” manages to quickly exit, changing the value of variable “a” before the “print” command (main) executes–thereby printing “hello, world”.
  • The goroutine is “slow”, and main() prints an empty string and exits.

Could someone shed some light on why the article says that the program might crash or “do something else”?

They are saying that the behavior is undefined and the program could, under the right circumstances, do something else. What that is is undefined and dependent on many possible things variables, like the contents of memory, the OS being used, the CPU architecture being used, etc.

The current implementation of a string can be seen in reflect.StringHeader, and is essentially:

type string struct {
    data unsafe.Pointer
    length int
}

So a string is 2 machine words and the words are not written atomically. Both the compiler and hardware platforms are allowed to reorder reads from/writes to memory, so it’s possible that the length could be written while the data is still a pointer to null. The subsequent print could essentially do this: Go Playground - The Go Programming Language

2 Likes