What is causing this data race? - go

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()
}
}

Related

Counter that rotates that is safe for concurrent use

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

Golang concurrency write to variable - why this code works?

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?

Most simple Go race-condition example?

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

Concurrent access to variable without lock

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.

Understanding Go's memory model [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
A seemingly clever trick to avoid locking in concurrent C code goes like this: I have a global variable ptr which points to a mystruct and I want to update that structure. So I'll allocate a new mystruct, fill the data in and only then I'll make the change visible to the world by pointing ptr to the new mystruct object.
This is incorrect as it depends on the ordering of writes and there's no guarantee that the write to ptr will become visible to other threads after all stores to the new mystruct have taken place. Therefore, the new mystruct object can be returned partially initialized.
My question is: can this happen in Go, too? I think it can, but I have to say I found The Go Memory Model a little incomprehensible.
I wrote a bit of Go code to test it out, but on my machine, the bad behavirour does not manifest itself:
package main
import (
"fmt"
"time"
)
type mystruct struct {
a int
b int
}
var (
ptr *mystruct
counter int
)
func writer() {
for {
counter += 1
s := mystruct{a: counter, b: counter}
ptr = &s
}
}
func reader() {
time.Sleep(time.Millisecond)
for {
if ptr.a != ptr.b {
fmt.Println("Oh no, I'm so buggy!")
}
}
}
func main() {
go writer()
go reader()
select {}
}
This of course proves nothing.
Can you please provide a very brief comparison of memory guarantees provided by Go's goroutines with (almost no guarantees) provided by a POSIX thread in C?
The Go Memory Model
Version of May 31, 2014
Advice
If you must read the rest of this document to understand the behavior
of your program, you are being too clever.
Don't be clever.
Introducing the Go Race Detector
I [David] wrote a bit of Go code to test it out.
Your Go program has data races. The results are undefined.
$ go run -race david.go
==================
WARNING: DATA RACE
Read at 0x000000596cc0 by goroutine 7:
main.reader()
/home/peter/gopath/src/david.go:29 +0x4b
Previous write at 0x000000596cc0 by goroutine 6:
main.writer()
/home/peter/gopath/src/david.go:22 +0xf8
Goroutine 7 (running) created at:
main.main()
/home/peter/gopath/src/david.go:37 +0x5a
Goroutine 6 (running) created at:
main.main()
/home/peter/gopath/src/david.go:36 +0x42
==================
==================
WARNING: DATA RACE
Read at 0x00c0000cc270 by goroutine 7:
main.reader()
/home/peter/gopath/src/david.go:29 +0x5b
Previous write at 0x00c0000cc270 by goroutine 6:
main.writer()
/home/peter/gopath/src/david.go:21 +0xd2
Goroutine 7 (running) created at:
main.main()
/home/peter/gopath/src/david.go:37 +0x5a
Goroutine 6 (running) created at:
main.main()
/home/peter/gopath/src/david.go:36 +0x42
==================
==================
WARNING: DATA RACE
Read at 0x00c0000cda38 by goroutine 7:
main.reader()
/home/peter/gopath/src/david.go:29 +0x7f
Previous write at 0x00c0000cda38 by goroutine 6:
main.writer()
/home/peter/gopath/src/david.go:21 +0xd2
Goroutine 7 (running) created at:
main.main()
/home/peter/gopath/src/david.go:37 +0x5a
Goroutine 6 (running) created at:
main.main()
/home/peter/gopath/src/david.go:36 +0x42
==================
<<SNIP>>
Your Go program: david.go:
package main
import (
"fmt"
"time"
)
type mystruct struct {
a int
b int
}
var (
ptr *mystruct
counter int
)
func writer() {
for {
counter += 1
s := mystruct{a: counter, b: counter}
ptr = &s
}
}
func reader() {
time.Sleep(time.Millisecond)
for {
if ptr.a != ptr.b {
fmt.Println("Oh no, I'm so buggy!")
}
}
}
func main() {
go writer()
go reader()
select {}
}
Playground: https://play.golang.org/p/XKywmzrRRRw

Resources