Go Race Detector Gotcha With Value Receivers
I ran into the following race detector error:
1 2 3 4 5 6 7 8 9 10 11 12
WARNING: DATA RACE
Write by goroutine 44:
github.com/couchbaselabs/sg-replicate.stateFnActiveFetchCheckpoint()
/Users/tleyden/Development/gocode/src/github.com/couchbaselabs/sg-replicate/replication_state.go:53 +0xb1d
github.com/couchbaselabs/sg-replicate.(*Replication).processEvents()
/Users/tleyden/Development/gocode/src/github.com/couchbaselabs/sg-replicate/synctube.go:120 +0xa3
Previous read by goroutine 27:
github.com/couchbaselabs/sg-replicate.(*Replication).GetStats()
<autogenerated>:24 +0xef
github.com/couchbase/sync_gateway/base.(*Replicator).populateActiveTaskFromReplication()
/Users/tleyden/Development/gocode/src/github.com/couchbase/sync_gateway/base/replicator.go:241 +0x145
Goroutine 44 was running this code:
1 2 3
func (r *Replication) shutdownEventChannel() {
r.EventChan = nil
}
and nil’ing out the r.EventChan field.
While goroutine 27 was calling this code on the same *Replication
instance:
1 2 3
func (r Replication) GetStats() ReplicationStats {
return r.Stats
}
It didn’t make sense, because they were accessing different fields of the Replication
— one was writing to r.EventChan
while the other was reading from r.Stats
.
Then I changed the GetStats()
method to this:
1 2 3
func (r Replication) GetStats() ReplicationStats {
return ReplicationStats{}
}
and it still failed!
I started wandering around the Couchbase office looking for help, and got Steve Yen to help me.
He was asking me about using a pointer receiver vs a value receiver here, and then we realized that by using a value reciever it was copying all the fields, and therefore reading all of the fields, including the r.EventChan
field that the other goroutine was concurrently writing to! Hence, the data race that was subtly caused by using a value receiver..
The fix was to convert this over to a pointer reciever, and the data race disappeared!
1 2 3
func (r *Replication) GetStats() ReplicationStats {
return r.Stats
}