I have a server , which deal the connection Session
like this
type Session struct {
conn *net.TCPConn //the tcp connection from client
recvChan chan []byte
closeNotiChan chan bool
ok bool
lock sync.Mutex
}
func (sess *Session) Close() {
sess.conn.Close()
sess.lock.Lock()
if sess.ok {
sess.ok = false
close(sess.closeNotiChan)
}
sess.lock.Unlock()
}
func (sess *Session) handleRecv() {
defer func() {
sess.Close()
}()
ch := sess.recvChan
header := make([]byte, 2)
for {
/**block until recieve len(header)**/
n, err := io.ReadFull(sess.conn, header)
if n == 0 && err == io.EOF {
//Opposite socket is closed
log.Warn("Socket Read EOF And Close", sess)
break
} else if err != nil {
//Sth wrong with this socket
log.Warn("Socket Wrong:", err)
break
}
//body lenght
size := binary.LittleEndian.Uint16(header) + 4
data := make([]byte, size)
n, err = io.ReadFull(sess.conn, t.Data)
if n == 0 && err == io.EOF {
log.Warn("Socket Read EOF And Close", sess)
break
} else if err != nil {
log.Warn("Socket Wrong:", err)
break
}
ch <- t //#1
}
}
func (sess *Session) handleDispatch() {
defer func() {
sess.Close()
close(sess.recvChan)//#2
for msg := range sess.recvChan {
//do something
}
}()
for {
select {
case msg, ok := <-sess.recvChan:
if ok {
//do something
}
case <-sess.closeNotiChan:
return
}
}
}
func StartClient() {//deal a new connection
var client Session
client.conn = connection
client.recvChan = make(chan []byte, 64)
client.closeNotiChan = make(chan bool)
client.ok = true
go client.handleRecv()
client.handleDispatch()
}
receiving data and dispatching data are in different goroutines.Now I have a problem. When a connection close,there is a data race ,#1
and #2
,something like the following(I don't post complete code)
WARNING: DATA RACE
Write by goroutine 179:
runtime.closechan()
/usr/local/go/src/runtime/chan.go:257 +0x0
sanguo/gameserver.func·004()
/data/mygo/src/sanguo/gameserver/session.go:149 +0xc7
sanguo/gameserver.(*Session).handleDispatch()
/data/mygo/src/sanguo/gameserver/session.go:173 +0x413
sanguo/gameserver.(*Session).Start()
/data/mygo/src/sanguo/gameserver/session.go:223 +0xc6
sanguo/gameserver.handleClient()
/data/mygo/src/sanguo/gameserver/gameserver.go:79 +0x43
Previous read by goroutine 126:
runtime.chansend()
/usr/local/go/src/runtime/chan.go:83 +0x0
sanguo/gameserver.(*Session).handleRecv()
/data/mygo/src/sanguo/gameserver/session.go:140 +0x1171
So, My problem is that how can I close the recvChan
elegently and effectively ,without "data race" and panic
.