Reputation: 587
so I am using gorillas websocket library and I'm building a websocket server when I receive a connection I create 2 go routines one to read incoming messages from the client and another 1 to listen for in coming messages sent to the channel and then send them to the client.
func (p *Player) EventLoop() {
l4g.Info("Starting player %s event loop", p)
go p.readFromSocket()
go p.writeToSocket()
//blocks until we receive an interrupt from the read channel
<-p.closeEventChan
p.cleanup() //nothing else to do so lets cleanup
}
func (p *Player) writeToSocket() {
for m := range p.writeChan {
if p.conn == nil {
break
}
if reflect.DeepEqual(network.Packet{}, m) == true {
break
}
if err := p.conn.WriteJSON(m); err != nil {
break
}
}
p.closeEventChan <- true
}
func (p *Player) readFromSocket() {
for {
if p.conn == nil {
break
}
m := network.Packet{}
if err := p.conn.ReadJSON(m); err != nil {
break
}
}
p.closeEventChan <- true
}
func (p *Player) Cleanup() {
//make sure the channels get a close message which will break us out of the go routines
close(p.writeChan)
close(p.closeEventChan)
//only close the connection if we have a connection
if p.conn != nil {
p.conn.Close()
p.conn = nil
}
}
my problem is if we leave the readFromSocket()
loop Cleanup()
is called however we never leave the writeToSocket()
loop ! this problem can be simpler demonstrated in this go playground https://play.golang.org/p/49bh7bbbG-
how can we fix this so if we leave the writeToSocket()
loop we also leave the readFromSocket()
loop and vice vesa?
I was under the impression that this would work as if you call close on a channel (close(p.writeChan)
) the default value that the channel accepts will be sent
Upvotes: 1
Views: 1181
Reputation: 22196
You can usually do this with a shared quit channel, and some counting.
func (p *Player) writeToSocket(quit <-chan struct{})
defer func() {
p.closeEventChan <- true
}()
for {
select {
case <-quit:
return
case m := <-p.writeChan:
// Do stuff
// Case where writeChan is closed, but quit isn't
default:
return
}
}
}
func (p *Player) readFromSocket(quit <-chan struct{})
defer func() {
p.closeEventChan <- true
}()
for {
select {
case <-quit:
return
default:
// Do stuff
}
}
}
func (p *Player) EventLoop() {
l4g.Info("Starting player %s event loop", p)
quit := make(chan struct{})
go p.readFromSocket(quit)
go p.writeToSocket(quit)
//blocks until we receive an interrupt from the read channel
<-p.closeEventChan
close(quit)
// This is superfluous here, but in a different case
// you loop over the remaining number of workers using a NumWorkers variable
for i := 0; i < 1; i++ {
<-p.closeEventChan
}
p.cleanup() //nothing else to do so lets cleanup
}
The idea here is that:
Upvotes: 3