Wednesday, August 4, 2021

GO and Race Condition


 


A race condition is defined as:

race condition or race hazard is the condition of an electronicssoftware, or other system where the system's substantive behavior is dependent on the sequence or timing of other uncontrollable events. It becomes a bug when one or more of the possible behaviors is undesirable.

        quoted from the Wikipedia site.


Go includes a race condition detector that is very simple to use:



$ go test -race mypkg    // test the package
$ go run -race mysrc.go  // compile and run the program
$ go build -race mycmd   // build the command
$ go install -race mypkg // install the package

        quoted from the Go lang blog.


I've used the race condition detector on my code, and found warnings about locations that surprised me. See the following code example:



var count int

func main() {
go update()
for {
fmt.Println(count)
time.Sleep(time.Second)
}
}

func update() {
for {
time.Sleep(time.Second)
count++
}
}



I got a warning about the count global variable, and the reason is that:

Programs that modify data being simultaneously accessed by multiple goroutines must serialize such access.

        quoted from the Go lang site.


But then, I thought to myself: "I don't care if I get an outdated value; I do not need a 100% accuracy here. I only want to get an update sometime, and I don't want to spend CPU time on a synchronization mutex in such a case".


So I had posted a question in StackOverflow, and it seemed to annoy some people, but all I wanted is to understand if this is indeed a bug, or am I just going to get outdated values. And the answer I got from everyone is that this is a bug. But I could not understand why.

They claimed that the code might not just get outdated values, but it can also crash, and do anything unexpected. 


So I decided to run some tests, and finally got to this version, where I've increased the speed of the update() Go routine, and let the main() print the status once in a second.



var count int32

func main() {
go update()

lastPrinted := time.Now()
for {
now := time.Now()
if now.Sub(lastPrinted) > time.Second {
fmt.Printf("count %v\n", count)
lastPrinted = now
}
}
}

func update() {
for {
count++
}
}


Now, the output is ALWAYS:



count 0
count 0
...  
  


And now I am a true believer that the race detector reports should never be ignored...



No comments:

Post a Comment