My goal is to create a simple websocket server. I use chan to distribute the message e.g by invoking <-messageChan
. the messageChan
have many writers and readers.
However, this StackOverflow question scares me of causing an unintentionally deadlock.
What I've did:
- Create a test that essentially do:
- populate a
chan int
with 0 to 1000. - create 100 goroutine to invoke
<-chan
and add it to amap[int]bool
. - invoke
t.Fatal
iflen(map[int]bool)
is not 1000. In other words, race condition.
- populate a
However, the test did not fail. I am afraid, I did something wrong, and chan
can have deadlock.
The code
main_test.go
package main
import (
"log"
"sync"
"testing"
)
type MapMux struct {
sync.RWMutex
m map[int]bool
sync.WaitGroup
}
func (mux *MapMux) AddInt(i int) {
mux.RLock()
if _, isExist := mux.m[i]; isExist {
log.Fatal("race condition")
}
mux.RUnlock()
mux.Lock()
mux.m[i] = true
mux.Unlock()
mux.Done()
}
func TestChanRaceCondition(t *testing.T) {
l := 1000
c := make(chan int, l)
defer close(c)
for i := 0; i < l; i++ {
c <- i
}
mux := MapMux{sync.RWMutex{}, map[int]bool{}, sync.WaitGroup{}}
mux.Add(l)
for i := 0; i < 100; i++ {
go func(key int) {
for {
payload := <-c
log.Printf("go%d: %d", key, payload)
mux.AddInt(payload)
}
}(i)
}
mux.Wait()
if len(mux.m) != l {
t.Fatal("expected len:", l, ", actual len:", len(mux.m))
}
}
Edit:
- The code finds duplicates in the
map[int]bool
field. Edit: This is becausedefer close(c)
. I should have check is the channel was open for every operation.
Anyway, this is what I learn. Hope this help new Golangers.
Lesson learnt:
- it is okay to have many writers and many readers for one channel.
- always check
val, isOpen := <-chan
. IfisOpen
==false
, then stop using the channel. sync.RWMutex.RLock()
does not guarantee other goroutine from making changes to the map. So, becareful. This is how it should be done.func (mux *MapMux) AddInt(i int) { mux.RLock() if _, isExist := mux.m[i]; isExist { log.Fatal("race condition") } mux.RUnlock() mux.Lock() if _, isExist := mux.m[i]; isExist { log.Fatal("race condition") } mux.m[i] = true mux.Unlock() mux.Done() }