I have a struct containing a map that is accessed from different goroutines protected by mutex:
type Readings struct {
sync.Mutex
Timestamp time.Time
values map[meters.Measurement]float64
}
Still, it has a data race between Add
(write) and MarshalJSON
(read). The latter performs structure conversion:
func (r *Readings) Add(q QuerySnip) {
r.Lock()
defer r.Unlock()
r.Timestamp = q.Timestamp
if r.values == nil {
r.values = make(map[meters.Measurement]float64)
}
r.values[q.Measurement] = q.Value
}
func (r *Readings) MarshalJSON() ([]byte, error) {
r.Lock()
defer r.Unlock()
res := map[string]interface{}{
"Timestamp": r.Timestamp,
"Unix": r.Timestamp.Unix(),
}
if r.values == nil {
return json.Marshal(res)
}
for m, v := range r.values {
res[m.String()] = v
}
return json.Marshal(res)
}
This is the race:
WARNING: DATA RACE
Write at 0x00c000294900 by goroutine 11:
runtime.mapassign_fast64()
/usr/local/opt/go/libexec/src/runtime/map_fast64.go:92 +0x0
github.com/volkszaehler/mbmd/server.(*Readings).Add()
...
Previous read at 0x00c000294900 by goroutine 25:
runtime.mapiterinit()
/usr/local/opt/go/libexec/src/runtime/map.go:804 +0x0
github.com/volkszaehler/mbmd/server.(*Readings).MarshalJSON()
/Users/andig/htdocs/mbmd/server/datagram.go:126 +0x304
github.com/volkszaehler/mbmd/server.(*data).MarshalJSON()
...
I don't understand what might by racey here and I'm not able to replicate the race behaviour moving the code into a standalone test case.
It there any advise for further diagnosis?
Both comments are right. I had copied a readings, was calling Add on copy and MarshalJson on original. The values was shared, itself being not deep copied.