Reputation: 105
Adding lock to a program with race condition can solve the race condition and make the race detector quiet. How can Go's race detector be aware of lock?
Someone points out that "the race detector can only detect race conditions if and when they actually occur".
Consider the following program:
package main
import (
"sync"
"time"
)
func main() {
var a int
var wg sync.WaitGroup
workers := 2
wg.Add(workers)
for i := 1; i <= workers; i++ {
go func(sleep int) {
time.Sleep(time.Duration(sleep) * time.Second)
a = 1
wg.Done()
}(i * 5)
}
wg.Wait()
}
One goroutine sleeps for 5 seconds, another sleeps for 10 seconds, they don't write a
at the same time in most cases, but the race detector prints the race condition warning every time. Why?
Upvotes: 5
Views: 698
Reputation: 418297
The race detector does not analyze the source code, it doesn't know that you added locks in the source code.
The race detector works at runtime:
When the
-race
command-line flag is set, the compiler instruments all memory accesses with code that records when and how the memory was accessed, while the runtime library watches for unsynchronized accesses to shared variables.
Because of this design, the race detector can only detect race conditions if and when they actually occur. So when you add proper locking / synchronization, the race condition will not happen (the if condition will not be met), and so no warning will be printed.
See this blog post for more details: Introducing the Go Race Detector
And this article: Data Race Detector
Edit for your example:
It may be that your 2 goroutines will never reach the point where they write the shared variable a
at the same physical time (because the code runs so fast and the sleep time is relatively huge), but they run concurrently, in different goroutines, without explicit synchronization (a synchronization point may be channel communication, mutex lock/unlock etc.).
The race condition does not imply that access to a shared variable does happen at the same time (one of which must be a write). The race condition is also met if access to a shared variable happens concurrently (from multiple goroutines), without synchronization. This can be detected at runtime by the race detector (due to the instrumented memory access code).
Code generated by the compiler is allowed to use cached instances of the a
variable in multiple goroutines, the runtime only has to guarantee that the cached instances are "refreshed" or disposed of if a synchronization point is reached. For details, see The Go Memory Model.
Also note that time.Sleep()
does not guarantee that execution will continue right after the specified duration, only that execution will be suspended for at least the specified duration (so the execution may continue at a later time):
Sleep pauses the current goroutine for at least the duration d.
Upvotes: 6
Reputation: 6844
The data race detector does not do static analysis. It is not aware of your lock. It’s empirical, it just notices that when you run your code with the lock, two threads are never writing to the same value at the same time (or one writing, one reading).
Upvotes: 0