I have had an occurence where two different go routines, read the same value from the same buffered channel, therefore creating a race condition.
Is this defined as possible somewhere ?
kind regards
klanghans
I have had an occurence where two different go routines, read the same value from the same buffered channel, therefore creating a race condition.
Is this defined as possible somewhere ?
kind regards
klanghans
Normally, only one receiver gets a value out of a channel. Here are some things I can think of:
Are you closing the channel? When you close a channel, all receivers “receive” a zero value. You can use a 2-value channel receive: value, ok := <-channel
and check ok
to see if the value was actually received or if the channel was just closed.
Are you using the unsafe
package to interact with channels anywhere to bypass the mutex that protects the channel?
Is it actually the same channel? Are you using some message queuing package whose API exposes a queue as a Go channel? If so, they might be implemented like this:
(Your server code) -- go channel --> (their server) -- network --> (their client) -- go channel --> (Your client code)
$ cat racer.go
package main
import "time"
func main() {
i := 42
ch := make(chan *int, 2)
go func() { i := <-ch; *i++ }()
go func() { i := <-ch; *i++ }()
ch <- &i
ch <- &i
time.Sleep(100 * time.Millisecond)
}
.
$ go run -race racer.go
==================
WARNING: DATA RACE
Read at 0x00c000016100 by goroutine 6:
main.main.func1()
/home/petrus/racer.go:8 +0x64
Previous write at 0x00c000016100 by goroutine 7:
main.main.func2()
/home/petrus/racer.go:9 +0x7a
Goroutine 6 (running) created at:
main.main()
/home/petrus/racer.go:8 +0x98
Goroutine 7 (finished) created at:
main.main()
/home/petrus/racer.go:9 +0xba
==================
Found 1 data race(s)
exit status 66
$
The Go Programming Language Specification
Programs that modify data being simultaneously accessed by multiple goroutines must serialize such access.
To serialize access, protect the data with channel operations or other synchronization primitives such as those in the
sync
andsync/atomic
packages.If you must read the rest of this document to understand the behavior of your program, you are being too clever.
Pointers! Good point! @klanghans If you send the same pointer into a channel twice, then two receiving goroutines can each get a copy of the pointer and you’ll introduce a race condition if both goroutines dereference it at the same time.
At any time without an intervening synchronization operation.
$ cat racer.go
package main
import "time"
func main() {
i := 42
ch := make(chan *int, 2)
go func() { i := <-ch; *i++ }()
ch <- &i
time.Sleep(1 * time.Second)
go func() { i := <-ch; *i++ }()
ch <- &i
time.Sleep(100 * time.Millisecond)
}
.
$ go run -race racer.go
==================
WARNING: DATA RACE
Read at 0x00c000016100 by goroutine 8:
main.main.func2()
/home/petrus/racer.go:11 +0x64
Previous write at 0x00c000016100 by goroutine 6:
main.main.func1()
/home/petrus/racer.go:8 +0x7a
Goroutine 8 (running) created at:
main.main()
/home/petrus/racer.go:11 +0xe9
Goroutine 6 (finished) created at:
main.main()
/home/petrus/racer.go:8 +0x98
==================
Found 1 data race(s)
exit status 66
$
Thank you very much for your support, ideas and information.
Since i couldnt find anything in the specs, i wrote some tests to confirm:
It turned out, that if you are sending structs (pointers) over a channel and copy data into that struct you have to make sure, dereference the copy beforehand, otherwise a race condition occurs.
Lesson learned!
Thank you all for your support
kind regards
klanghans
This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.