Why is following code:
package main
import (
"fmt"
"time"
)
func main() {
str := "ab"
strPtr := &str
go func() {
str2 := "cd"
strPtr = &str2
}()
time.Sleep(2 * time.Second)
fmt.Printf("current address %p\n", strPtr)
}
producing a data race when run with go run -race main.go
? Datarace looks like this:
==================
WARNING: DATA RACE
Read at 0x00c0000b4018 by main goroutine:
main.main()
/Users/***/main/main.go:16 +0x124
Previous write at 0x00c0000b4018 by goroutine 7:
main.main.func1()
/Users/***/main/main.go:13 +0x7c
Goroutine 7 (finished) created at:
main.main()
/Users/***/main/main.go:11 +0x110
==================
current address 0xc000180000
Found 1 data race(s)
exit status 66
Datarace is reported for read from address 0x00c0000b4018
on line fmt.Printf("current address %p\n", strPtr)
, but at that time the strPtr
already points to different address 0xc000180000
! How comes?
And even if, by some miracle, there would be a datarace, what I am doing here is assigning a pointer, which based on go memory model should be atomic, re-quoting via this SO answer:
As of today, the version on June 6, 2022 of the Go memory model guarantees that a memory access not larger than a machine word is atomic.
Otherwise, a read r of a memory location x that is not larger than a machine word must observe some write w such that r does not happen before w and there is no write w' such that w happens before w' and w' happens before r. That is, each read must observe a value written by a preceding or concurrent write.
It is a datarace because you have a concurrent memory read/write operation without explicit synchronization. The pointer assignment in the goroutine is concurrent with the pointer read in the main goroutine.
The fact that a memory write for a pointer-size value is atomic is not relevant to a memory race. There is a race simply because the read operation can see the value of the pointer before the assignment, or the value after it, because there is no happened-before relationship between the memory-read and memory-write. So it is unlikely, but theoretically possible to observe two values for that pointer.