0

I built the following code with "-race" flag and ran it(go version go1.14.1 linux/amd64), and some data races were reported(see below). Sometimes only one data race was reported and sometimes three. The data race between line 24 and 35 is understandable, but I can't understand why a data race between line 24 and 40 is reported.

  1 package main
  2 
  3 import (
  4         "sync"
  5 )
  6 
  7 var (
  8         m          = make(map[string]string)
  9         pm         = &m
 10         updateLock = sync.RWMutex{}
 11 )
 12 
 13 func main() {
 14         wg := &sync.WaitGroup{}
 15         wg.Add(2)
 16         go func() {
 17                 defer wg.Done()
 18                 handle()
 19         }()
 20 
 21         go func() {
 22                 defer wg.Done()
 23                 //updateLock.RLock()
 24                 if _, ok := (*pm)["test"]; ok {
 25                 }
 26                 //updateLock.RUnlock()
 27         }()
 28         wg.Wait()
 29 }
 30 
 31 func handle() {
 32         newMap := make(map[string]string)
 33         update(&newMap)
 34         updateLock.Lock()
 35         pm = &newMap
 36         updateLock.Unlock()
 37 }
 38 
 39 func update(ptrMap *map[string]string) {
 40         (*ptrMap)["test"] = "test"
 41 }

I think the map created in function handle which is passed to function update to be modified at line 40, is different from the map that is read at line 24. The pointer replacement happens after the update is done, so why is there such a data race:

==================
WARNING: DATA RACE
Read at 0x00c000070030 by goroutine 7:
  runtime.mapaccess2_faststr()
      /home/vagrant/.go/src/runtime/map_faststr.go:107 +0x0
  main.main.func2()
      /vagrant/go_projects/src/learn/race/main.go:24 +0xd1

Previous write at 0x00c000070030 by goroutine 6:
  runtime.mapassign_faststr()
      /home/vagrant/.go/src/runtime/map_faststr.go:202 +0x0
  main.update()
      /vagrant/go_projects/src/learn/race/main.go:40 +0xba
  main.handle()
      /vagrant/go_projects/src/learn/race/main.go:33 +0x7f
  main.main.func1()
      /vagrant/go_projects/src/learn/race/main.go:18 +0x5f

Goroutine 7 (running) created at:
  main.main()
      /vagrant/go_projects/src/learn/race/main.go:21 +0xc4

Goroutine 6 (finished) created at:
  main.main()
      /vagrant/go_projects/src/learn/race/main.go:16 +0xa2
==================

P.S. When line 23 and 26 are uncommented, the race is gone.

  • `you have nothing coordinating those 2 goroutines`, that's true. But the pointer replacement and update are executed in the same goroutine and they should be executed sequentially. – Francis Ye Apr 26 '20 at 02:19
  • That data race is on the `pm` variable, you are writing it in one go routine, and reading it in the other. – JimB Apr 26 '20 at 02:37

1 Answers1

2

handle() is writing to pm (the pointer pm, not the map contents) while the second goroutine is reading from pm without locking in. That's the reason why a race is reported. When you access pm using the lock, the goroutine cannot read pm during a write.

There is a chance that when the goroutine reads from pm, pm is not initialized yet.

The race detector detects locked/unlocked read/write access to the map. The reading goroutine is reading from a map without a lock, and the writing goroutine is writing to the same map without a lock. That's what the race detector is complaining about.

Burak Serdar
  • 46,455
  • 3
  • 40
  • 59
  • Thank you for your answer. But I think this explains the reported race between line 24 and 35, not the one between line 24 and 40, which seems to indicate concurrent read and write at the map. – Francis Ye Apr 25 '20 at 17:53
  • Updated the answer to include the case for line 40. – Burak Serdar Apr 25 '20 at 18:08