try to shutdown more safely so callbacks, etc don't try and use the read/write channels after they are closed

This commit is contained in:
Andrew Montgomery-Hurrell 2014-02-09 12:02:05 +00:00
parent 09ea5672e2
commit 0b6c954b52
2 changed files with 85 additions and 67 deletions

150
irc.go
View File

@ -23,59 +23,65 @@ func (irc *Connection) readLoop() {
br := bufio.NewReaderSize(irc.socket, 512) br := bufio.NewReaderSize(irc.socket, 512)
for { for {
// Set a read deadline based on the combined timeout and ping frequency select {
// We should ALWAYS have received a response from the server within the timeout case <-irc.endread:
// after our own pings irc.readerExit <- true
if irc.socket != nil { return
irc.socket.SetReadDeadline(time.Now().Add(irc.Timeout + irc.PingFreq)) default:
} // Set a read deadline based on the combined timeout and ping frequency
// We should ALWAYS have received a response from the server within the timeout
msg, err := br.ReadString('\n') // after our own pings
if irc.socket != nil {
// We got past our blocking read, so bin timeout irc.socket.SetReadDeadline(time.Now().Add(irc.Timeout + irc.PingFreq))
if irc.socket != nil {
var zero time.Time
irc.socket.SetReadDeadline(zero)
}
if err != nil {
irc.Error <- err
break
}
irc.lastMessage = time.Now()
msg = msg[:len(msg)-2] //Remove \r\n
event := &Event{Raw: msg}
if msg[0] == ':' {
if i := strings.Index(msg, " "); i > -1 {
event.Source = msg[1:i]
msg = msg[i+1 : len(msg)]
} else {
irc.log.Printf("Misformed msg from server: %#s\n", msg)
} }
if i, j := strings.Index(event.Source, "!"), strings.Index(event.Source, "@"); i > -1 && j > -1 { msg, err := br.ReadString('\n')
event.Nick = event.Source[0:i]
event.User = event.Source[i+1 : j] // We got past our blocking read, so bin timeout
event.Host = event.Source[j+1 : len(event.Source)] if irc.socket != nil {
var zero time.Time
irc.socket.SetReadDeadline(zero)
} }
if err != nil {
irc.Error <- err
break
}
irc.lastMessage = time.Now()
msg = msg[:len(msg)-2] //Remove \r\n
event := &Event{Raw: msg}
if msg[0] == ':' {
if i := strings.Index(msg, " "); i > -1 {
event.Source = msg[1:i]
msg = msg[i+1 : len(msg)]
} else {
irc.log.Printf("Misformed msg from server: %#s\n", msg)
}
if i, j := strings.Index(event.Source, "!"), strings.Index(event.Source, "@"); i > -1 && j > -1 {
event.Nick = event.Source[0:i]
event.User = event.Source[i+1 : j]
event.Host = event.Source[j+1 : len(event.Source)]
}
}
args := strings.SplitN(msg, " :", 2)
if len(args) > 1 {
event.Message = args[1]
}
args = strings.Split(args[0], " ")
event.Code = strings.ToUpper(args[0])
if len(args) > 1 {
event.Arguments = args[1:len(args)]
}
/* XXX: len(args) == 0: args should be empty */
irc.RunCallbacks(event)
} }
args := strings.SplitN(msg, " :", 2)
if len(args) > 1 {
event.Message = args[1]
}
args = strings.Split(args[0], " ")
event.Code = strings.ToUpper(args[0])
if len(args) > 1 {
event.Arguments = args[1:len(args)]
}
/* XXX: len(args) == 0: args should be empty */
irc.RunCallbacks(event)
} }
irc.readerExit <- true irc.readerExit <- true
@ -83,27 +89,35 @@ func (irc *Connection) readLoop() {
func (irc *Connection) writeLoop() { func (irc *Connection) writeLoop() {
for { for {
b, ok := <-irc.pwrite select {
if !ok || b == "" || irc.socket == nil { case <-irc.endwrite:
break irc.writerExit <- true
} return
default:
b, ok := <-irc.pwrite
if !ok || b == "" || irc.socket == nil {
irc.writerExit <- true
return
}
if irc.Debug { if irc.Debug {
irc.log.Printf("--> %s\n", b) irc.log.Printf("--> %s\n", b)
} }
// Set a write deadline based on the time out // Set a write deadline based on the time out
irc.socket.SetWriteDeadline(time.Now().Add(irc.Timeout)) irc.socket.SetWriteDeadline(time.Now().Add(irc.Timeout))
_, err := irc.socket.Write([]byte(b)) _, err := irc.socket.Write([]byte(b))
// Past blocking write, bin timeout // Past blocking write, bin timeout
var zero time.Time var zero time.Time
irc.socket.SetWriteDeadline(zero) irc.socket.SetWriteDeadline(zero)
if err != nil { if err != nil {
irc.Error <- err irc.Error <- err
break irc.writerExit <- true
return
}
} }
} }
irc.writerExit <- true irc.writerExit <- true
@ -206,9 +220,11 @@ func (irc *Connection) GetNick() string {
// Sends all buffered messages (if possible), // Sends all buffered messages (if possible),
// stops all goroutines and then closes the socket. // stops all goroutines and then closes the socket.
func (irc *Connection) Disconnect() { func (irc *Connection) Disconnect() {
irc.endping <- true
irc.endwrite <- true
irc.endread <- true
close(irc.pwrite) close(irc.pwrite)
close(irc.pread) close(irc.pread)
irc.endping <- true
<-irc.readerExit <-irc.readerExit
<-irc.writerExit <-irc.writerExit
@ -267,6 +283,8 @@ func IRC(nick, user string) *Connection {
writerExit: make(chan bool), writerExit: make(chan bool),
pingerExit: make(chan bool), pingerExit: make(chan bool),
endping: make(chan bool), endping: make(chan bool),
endread: make(chan bool),
endwrite: make(chan bool),
Version: VERSION, Version: VERSION,
KeepAlive: 4 * time.Minute, KeepAlive: 4 * time.Minute,
Timeout: 1 * time.Minute, Timeout: 1 * time.Minute,

View File

@ -26,7 +26,7 @@ type Connection struct {
netsock net.Conn netsock net.Conn
pread, pwrite chan string pread, pwrite chan string
readerExit, writerExit, pingerExit chan bool readerExit, writerExit, pingerExit chan bool
endping chan bool endping, endread, endwrite chan bool
nick string //The nickname we want. nick string //The nickname we want.
nickcurrent string //The nickname we currently have. nickcurrent string //The nickname we currently have.