Race Detector
- Go provides race detector tool for finding race conditions in Go code.
- Binary needs to be race enabled
- When race behaviour is detected a warning is printed
- Race enabled binary will 10 times slower and consume 10 times more memory.
- Integration tests and load tests are good candidates to test with binary with race enabled.
# test the package
go test -race mypkg
# compile and run the program
go run -race mysrc.go
# build the command
go build -race mycmd
# install the package
go install -race mypkg
Exercises
go run - race concurrency/06_Sync_Package/exercises/07_race_problem.go
948.57048ms
==================
WARNING: DATA RACE
Read at 0x00c000136018 by goroutine 8:
main.main.func1()
/Users/kimi/go/src/MyGoNote/concurrency/06_Sync_Package/exercises/07_race_problem.go:17 +0xd4
Previous write at 0x00c000136018 by main goroutine:
main.main()
/Users/kimi/go/src/MyGoNote/concurrency/06_Sync_Package/exercises/07_race_problem.go:15 +0x158
Goroutine 8 (running) created at:
time.goFunc()
/usr/local/Cellar/go/1.17.5/libexec/src/time/sleep.go:180 +0x49
==================
1.034536491s
1.701154081s
1.937634052s
2.225741529s
2.776342333s
3.409605905s
3.741838895s
3.926345687s
4.408004933s
Found 1 data race(s)
exit status 66
Problem
https://go.dev/play/p/p1zlAJyoAhh
package main
import (
"fmt"
"math/rand"
"time"
)
//TODO: identify the data race
// fix the issue.
func main() {
start := time.Now()
var t *time.Timer
t = time.AfterFunc(randomDuration(), func() {
fmt.Println(time.Now().Sub(start))
t.Reset(randomDuration())
})
time.Sleep(5 * time.Second)
}
func randomDuration() time.Duration {
return time.Duration(rand.Int63n(1e9))
}
//----------------------------------------------------
// (main goroutine) -> t <- (time.AfterFunc goroutine)
//----------------------------------------------------
// (working condition)
// main goroutine..
// t = time.AfterFunc() // returns a timer..
// AfterFunc goroutine
// t.Reset() // timer reset
//----------------------------------------------------
// (race condition- random duration is very small)
// AfterFunc goroutine
// t.Reset() // t = nil
// main goroutine..
// t = time.AfterFunc()
//----------------------------------------------------
Solution
https://go.dev/play/p/IdgSE9tn3yM
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
start := time.Now()
reset := make(chan bool)
var t *time.Timer
t = time.AfterFunc(randomDuration(), func() {
fmt.Println(time.Now().Sub(start))
reset <- true
})
for time.Since(start) < 5*time.Second {
<-reset
t.Reset(randomDuration())
}
}
// return random duration between 0~1 seconds
func randomDuration() time.Duration {
return time.Duration(rand.Int63n(1e9))
}