I need a simple Go code sample which will definitely run the program into an race-condition.
Any ideas?
The original question:
I need a simple Go code sample which will definitely run the program
into an race-condition.
For example,
racer.go:
package main
import (
"time"
)
var count int
func race() {
count++
}
func main() {
go race()
go race()
time.Sleep(1 * time.Second)
}
Output:
$ go run -race racer.go
==================
WARNING: DATA RACE
Read at 0x00000052ccf8 by goroutine 6:
main.race()
/home/peter/gopath/src/racer.go:10 +0x3a
Previous write at 0x00000052ccf8 by goroutine 5:
main.race()
/home/peter/gopath/src/racer.go:10 +0x56
Goroutine 6 (running) created at:
main.main()
/home/peter/gopath/src/racer.go:15 +0x5a
Goroutine 5 (finished) created at:
main.main()
/home/peter/gopath/src/racer.go:14 +0x42
==================
Found 1 data race(s)
exit status 66
$
package main
import (
"fmt"
)
func main() {
i := 0
// Run forever to make it to show race condition
for {
var x, y int
// Set x to 60
go func(v *int) {
*v = 60
}(&x)
// Set y to 3
go func(v *int) {
*v = 3
}(&y)
/*
A race condition is when multiple threads are trying to access and manipulate the same variable.
the code below is all accessing and changing the value.
Divide x (60) by y (3) and assign to z (42)...
Except if y is not assigned 3 before x is assigned 60,
y's initialized value of 0 is used,
Due to the uncertainty of the Goroutine scheduling mechanism, the results of the following program is unpredictable,
which causes a divide by zero exception.
*/
go func(v1 int, v2 int) {
fmt.Println(v1 / v2)
}(x, y)
i += 1
fmt.Printf("%d\n", i)
}
}
Run code using: go run -race .go
Related
I want to write something like this:
type RoundRobinList struct {
lst []string
idx uint32
}
// rr_list is a RoundRobinList
func loop(rr_list) {
start = rr_list.idx
rr_list.idx = (rr_list.idx + 1)%len(rr_list.lst)
print(rr_list.lst[idx])
}
If rr_list.lst = ["a", "b", "c"], and loop is called over and over, I would expect the following printed:
"a"
"b"
"c"
"a"
"b"
"c" ...
Is this safe? rr_list.idx = (rr_list.idx + 1)%len(rr_list.lst)
Whenever you have a read and write of a value that's not protected in some way (using a mutex, or some of the other things in the sync package), you have a race, and that means you can't use it safely in a concurrent setting.
Here, you're reading and writing the idx field of your RoundRobinList structure without any protection, so you'll have a race if you use it concurrently.
As a first line of defense you should understand the rules for memory safety and follow them carefully, and not write code unless you're pretty sure it's safe. As a second line of defense, you can use the race detector to find a lot of problems with lack of safety of concurrent access.
Here's a simple test case, in file a_test.go. I had to also fix some bugs in the code to get it to compile. It starts two goroutines which call loop 1000 times each on a shared RoundRobinList.
package main
import (
"sync"
"testing"
)
type RoundRobinList struct {
lst []string
idx uint32
}
func loop(rr *RoundRobinList) {
rr.idx = (rr.idx + 1) % uint32(len(rr.lst))
}
func TestLoop(t *testing.T) {
rr := RoundRobinList{[]string{"a", "b", "c"}, 0}
var wg sync.WaitGroup
wg.Add(2)
for n := 0; n < 2; n++ {
go func() {
for i := 0; i < 1000; i++ {
loop(&rr)
}
wg.Done()
}()
}
wg.Wait()
}
Running with go test -race ./a_test.go results in:
==================
WARNING: DATA RACE
Read at 0x00c0000b0078 by goroutine 9:
command-line-arguments.loop()
/mnt/c/Users/paul/Desktop/a_test.go:14 +0xa1
command-line-arguments.TestLoop.func1()
/mnt/c/Users/paul/Desktop/a_test.go:24 +0x78
Previous write at 0x00c0000b0078 by goroutine 8:
command-line-arguments.loop()
/mnt/c/Users/paul/Desktop/a_test.go:14 +0x4e
command-line-arguments.TestLoop.func1()
/mnt/c/Users/paul/Desktop/a_test.go:24 +0x78
Goroutine 9 (running) created at:
command-line-arguments.TestLoop()
/mnt/c/Users/paul/Desktop/a_test.go:22 +0x1cc
testing.tRunner()
/usr/local/go/src/testing/testing.go:992 +0x1eb
Goroutine 8 (finished) created at:
command-line-arguments.TestLoop()
/mnt/c/Users/paul/Desktop/a_test.go:22 +0x1cc
testing.tRunner()
/usr/local/go/src/testing/testing.go:992 +0x1eb
==================
--- FAIL: TestLoop (0.00s)
testing.go:906: race detected during execution of test
FAIL
FAIL command-line-arguments 0.006s
FAIL
I'm learning concurrency-related issues in Golang. I wrote some code:
package main
import (
"fmt"
"time"
)
func incr(num *int) {
*num = *num + 1
}
func main() {
var a = 0
for i := 0; i < 50; i++ {
go incr(&a)
}
incr(&a)
time.Sleep(1 * time.Second)
fmt.Println(a)
}
The result of this code is: 51
In this code I've declared a variable which I'm increasing in 50 running goroutines. What I've read and unsterstood this code should fail because multiple goroutines are writing to same memory address. In this case I should add sync.Mutex lock in order to fix that.
Code is available in the playground: https://play.golang.org/p/Tba9pfpxaHY
Could You explain what is really happening in this program?
Guess what? I ran your app and I get varying outputs: sometimes 49, sometimes 48, sometimes 50 (and sometimes 51).
If you run your app with the race detector enabled (go run -race play.go), it tells you have data races:
==================
WARNING: DATA RACE
Read at 0x00c00009a010 by goroutine 7:
main.incr()
/home/icza/gows/src/play/play.go:9 +0x3a
Previous write at 0x00c00009a010 by goroutine 6:
main.incr()
/home/icza/gows/src/play/play.go:9 +0x50
Goroutine 7 (running) created at:
main.main()
/home/icza/gows/src/play/play.go:17 +0x83
Goroutine 6 (finished) created at:
main.main()
/home/icza/gows/src/play/play.go:17 +0x83
==================
When you have data races, the behavior of your app is undefined. "Seemingly working sometimes" also fits into the "undefined" behavior, but undefined also means it can do anything else too.
See related questions:
Assign a map to another map is safety in golang?
Is it safe to read a function pointer concurrently without a lock?
golang struct concurrent read and write without Lock is also running ok?
Why does this code cause data race?
I have already used atomic add.
package main
import (
"sync/atomic"
"time"
)
var a int64
func main() {
for {
if a < 100 {
atomic.AddInt64(&a, 1)
go run()
}
}
}
func run() {
<-time.After(5 * time.Second)
atomic.AddInt64(&a, -1)
}
I run command go run --race with this code and get:
==================
WARNING: DATA RACE
Write at 0x000001150f30 by goroutine 8:
sync/atomic.AddInt64()
/usr/local/Cellar/go/1.11.2/libexec/src/runtime/race_amd64.s:276 +0xb
main.run()
/Users/flask/test.go:22 +0x6d
Previous read at 0x000001150f30 by main goroutine:
main.main()
/Users/flask/test.go:12 +0x3a
Goroutine 8 (running) created at:
main.main()
/Users/flask/test.go:15 +0x75
==================
Could you help me explain this?
And how to fix this warning?
Thanks!
You didn't use the atomic package at all places where you accessed the variable. All access must be synchronized to variables that are accessed from multiple goroutines concurrently, including reads:
for {
if value := atomic.LoadInt64(&a); value < 100 {
atomic.AddInt64(&a, 1)
go run()
}
}
With that change, the race condition goes away.
If you just want to inspect the value, you don't even need to store it in a variable, so you may simply do:
for {
if atomic.LoadInt64(&a) < 100 {
atomic.AddInt64(&a, 1)
go run()
}
}
Is the main() function a goroutine? For example, I've seen a crash stack trace like the below, which makes me ask:
goroutine 1 [running]: main.binarySearch(0x0, 0x61, 0x43,
0xc420043e70, 0x19, 0x19, 0x10)
/home/---/go/src/github.com/----/sumnum.go:22 +0x80 main.main()
/home/---/go/src/github.com/---/sumnum.go:13 +0xc1 exit status 2
Is the main function a goroutine?
No.
The main function is a function.
In contrast,
A goroutine is a lightweight thread of execution. (source).
So goroutines execute functions, but goroutines are not functions, and there is not a 1-to-1 relationship between goroutines and functions.
However...
The main() function is executed in the first (and at startup, only) goroutine, goroutine #1.
But as soon as that function calls another function, then the main goroutine is no longer executing the main function, and is instead executing some other function.
So it's clear that a goroutine and a function are entirely different entities.
Do not conflate goroutines with functions!!
Functions and goroutines are entirely different concepts. And thinking of them as the same thing will lead to countless confusion and problems.
Yes, the main function runs as a goroutine (the main one).
According to https://tour.golang.org/concurrency/1
A goroutine is a lightweight thread managed by the Go runtime.
go f(x, y, z)
starts a new goroutine running f(x, y, z) The evaluation of f, x, y, and z happens in the current goroutine and the execution of f happens in the new goroutine.
Goroutines run in the same address space, so access to shared memory must be synchronized. The sync package provides useful primitives, although you won't need them much in Go as there are other primitives.
So according to this official document the main is the current goroutine.
To be precise (literally) we could address the main as the current goroutine, so simply speaking it is a goroutine. (Note: Literally speaking the main() is a function which could run as a goroutine.)
Now let's count the number of goroutines using runtime.NumGoroutine():
As an example let's run 3 goroutines. Try it online:
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
fmt.Println(runtime.NumGoroutine()) // 3
time.Sleep(100 * time.Millisecond)
}
func init() {
go main()
go main()
}
Here the current goroutine runs the new goroutine, so here we have more than one goroutine, which executes main() again. Try it online:
package main
import (
"fmt"
"runtime"
"sync/atomic"
"time"
)
func main() {
fmt.Println(runtime.NumGoroutine()) // 1 2 3 4
if atomic.LoadInt32(&i) <= 0 {
return
}
atomic.AddInt32(&i, -1)
go main()
time.Sleep(100 * time.Millisecond)
}
var i int32 = 3
Output:
1
2
3
4
Here we have one main goroutine plus 3 user called main goroutines, so total number of goroutines are 4 here.
Let's calculate factorial using main() (one goroutine - no synchronization needed). Try it online:
package main
import "fmt"
func main() {
if f <= 0 {
fmt.Println(acc)
return
}
acc *= f
f--
main()
}
var f = 5
var acc = 1
Output:
120
Note: The codes above are just for clearly showing my viewpoints and is not good for production use (Using global variables should not be the first choice).
Yes. Main func can spawn other goroutines, but "main" itself is one groutine.
package main
import (
"fmt"
"runtime"
)
func main() {
// Goroutine num includes main processing
fmt.Println(runtime.NumGoroutine()) // 1
// Spawn two goroutines
go func() {}()
go func() {}()
// Total three goroutines run
fmt.Println(runtime.NumGoroutine()) // 3
}
I was disturbed by a question,
should we add lock if only one thread write variable, and other thread just read variable?
so I write such code to test it
package main
import (
"fmt"
"runtime"
"sync"
"time"
)
var lock sync.RWMutex
var i = 0
func main() {
runtime.GOMAXPROCS(2)
go func() {
for {
fmt.Println("i am here", i)
time.Sleep(time.Second)
}
}()
for {
i += 1
}
}
The result is keep print i am here 0 even after second of time. I know a little about Memory barrier or cpu cache. but how could it be cache for such a long time? I think after a few time, it should read variable I already changed.
Can anyone who is master go or computer system could help answer, please?
Update: i know it is a wrong way to update variable like this, i want to know why it is undefined in cpu/memory view.
You have a data race. Therefore, the results are undefined.
package main
import (
"fmt"
"runtime"
"sync"
"time"
)
var lock sync.RWMutex
var i = 0
func main() {
runtime.GOMAXPROCS(2)
go func() {
for {
fmt.Println("i am here", i)
time.Sleep(time.Second)
}
}()
for {
i += 1
}
}
Output:
$ go run -race racer.go
==================
WARNING: DATA RACE
Read at 0x0000005e3600 by goroutine 6:
main.main.func1()
/home/peter/gopath/src/racer.go:17 +0x63
Previous write at 0x0000005e3600 by main goroutine:
main.main()
/home/peter/gopath/src/racer.go:22 +0x7b
Goroutine 6 (running) created at:
main.main()
/home/peter/gopath/src/racer.go:15 +0x4f
==================
i am here 3622
i am here 43165250
i am here 86147697
^Csignal: interrupt
$
References:
Data Race Detector
Benign Data Races: What Could Possibly Go Wrong?
should we add lock if only one thread write variable, and other thread just read variable?
Yes. Always. No arguing here.
Your test code proves and disproves nothing as its behaviour is undefined.
finally, i find this answers, i know with a data race you will get a undefined behave, but i want to know why it behave like that currently.
this snap code is because complier just remove Add function, it never add.
so we have lesson, if you write a undefined behave, you may got a moon - -
complier will treat you code as rubbish, it does not have any value.