yaodongen
yaodongen

Reputation: 31

how to create a real race condition in golang

problem description

Recently I learned the option -race to check the exist of race condition in go. The full command is go run -race xxx.go It really helped me a lot. But as with the code below, I think the check result is wrong, and tried a lot of method (My try to get a panic below) to get a REAL panic but failed. So I want to know whether the code is correct and the race check is wrong,or can you revise my code so that I can SEE a real panic. Thanks a lot.

The code

package main
import "fmt"

type myType struct {
    A int
}

func main(){
    c:=make(chan bool)
    x := new(myType)
    go func(){
        x = new(myType) // write to x
        c <- true
    }()
    _ = *x // read from x
    <-c
    fmt.Println("end")
}

The race check result

go run -race test.go

==================
WARNING: DATA RACE
Write at 0x00c00009c010 by goroutine 6:
  main.main.func1()
      /Users/yaodongen/test.go:12 +0x56

Previous read at 0x00c00009c010 by main goroutine:
  main.main()
      /Users/yaodongen/test.go:15 +0xe2

Goroutine 6 (running) created at:
  main.main()
      /Users/yaodongen/test.go:11 +0xd4
==================
end
Found 1 data race(s)
exit status 66

My point

I tried to find the reason for the race condition report.In a post(Chinese), it mentions that the operation a = in64(0) is not atomic. For example in one 32 bit Machine and the data like int64 may be 64 bit length, CPU could copy half of the data and be interruptted by others. In the following code (Prove the golang copy is not atomic), I write a code to prove its true. But in my case, the code x = new(myType) is to copy a pointer value, and I think it can be done in one CPU copy. In other word, the operation is atomic and will never reach race condition.

Prove the golang copy is not atomic

package main
import "fmt"
import "time"

func main(){
    var x = [...]int{1,1,1,1,1,1}
    c := make(chan int, 100)
    go func(){
        for i:=0;;i++{
            if i&1 == 0 {
                x = [...]int{2,2,2,2,2,2} // write to x
            }else{
                x = [...]int{1,1,1,1,1,1} // write to x
            }
            c<-0 // let other goroutine see the change of x
            <-c
        }
    }()
    go func(){
        for {
            d := x                    // read from x
            if d[0] != d[5] {
                fmt.Println(d)
                panic("error")        // proved the copy operation is not atomic
            }
            c<-0
            <-c
        }
    }()
    time.Sleep(time.Millisecond * 10000)
    fmt.Println("end")
}

My try to get a panic

But it failed, the code will panic if there exists a race condition (wrong memory address).

package main
import "fmt"
import "time"

type myType struct {
    A int
}

func main(){
    x := new(myType)
    c := make(chan int, 100)
    go func(){
        for {
            x = new(myType) // write to x
            c<-0
            <-c
        }
    }()
    for i:=0; i<4; i++{
        go func(){
            for {
                _ = *x // if exists a race condition, `*x` will visit a wrong memory address, and will panic
                c<-0
                <-c
            }
        }()
    }
    time.Sleep(time.Second * 10)
    fmt.Println("end")
}

Upvotes: 2

Views: 2329

Answers (1)

icza
icza

Reputation: 418585

Go's race detection never gives false positives. If it tells you there's a race, then there is a race. It might not recognize all races (they have to happen to be detectable), but what it finds is always positive (bugs in the race detector not counting).

The race condition in your example is clear and simple. You have 2 goroutines, one reads a variable and the other one writes it without synchronization. This is the recipe for a race condition.

Race conditions make your app unpredictable. A race condition's behavior is undefined. Any experienced behavior falls under undefined, including the lack of panic. Don't tempt the devil, if there's a race condition, use proper synchronization. End of story.

See Is it safe to read a function pointer concurrently without a lock?

Upvotes: 5

Related Questions