From f7a4f5d956b8bf72188acdcb2a863e042b40d490 Mon Sep 17 00:00:00 2001 From: Daniel Oaks Date: Tue, 18 Apr 2017 20:29:00 +1000 Subject: [PATCH] socket: Move to a timing-out send method that reduces goroutines and ensures QUIT/ERROR are sent --- irc/client.go | 10 +++++++--- irc/socket.go | 47 ++++++++++++++++++++++++++++------------------- 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/irc/client.go b/irc/client.go index 1eb1fa11..55ad339e 100644 --- a/irc/client.go +++ b/irc/client.go @@ -436,9 +436,13 @@ func (client *Client) ChangeNickname(nickname string) error { // Quit sends the given quit message to the client (but does not destroy them). func (client *Client) Quit(message string) { if !client.quitMessageSent { - client.Send(nil, client.nickMaskString, "QUIT", message) - client.Send(nil, "", "ERROR", message) - client.socket.Write("\r\n") + quitMsg := ircmsg.MakeMessage(nil, client.nickMaskString, "QUIT", message) + quitLine, _ := quitMsg.Line() + + errorMsg := ircmsg.MakeMessage(nil, "", "ERROR", message) + errorLine, _ := errorMsg.Line() + + client.socket.FinalData = quitLine + errorLine client.quitMessageSent = true } } diff --git a/irc/socket.go b/irc/socket.go index a55dafda..d7e7b4b6 100644 --- a/irc/socket.go +++ b/irc/socket.go @@ -31,6 +31,7 @@ type Socket struct { reader *bufio.Reader MaxSendQBytes uint64 + FinalData string // what to send when we die lineToSendExists chan bool linesToSend []string @@ -54,8 +55,8 @@ func (socket *Socket) Close() { } socket.Closed = true - // force close loop to happen - go socket.fillLineToSendExists(true) + // force close loop to happen if it hasn't already + go socket.timedFillLineToSendExists(200 * time.Millisecond) } // CertFP returns the fingerprint of the certificate provided by the client. @@ -119,15 +120,21 @@ func (socket *Socket) Write(data string) error { socket.linesToSendMutex.Lock() socket.linesToSend = append(socket.linesToSend, data) socket.linesToSendMutex.Unlock() - go socket.fillLineToSendExists(false) + + if !socket.Closed { + go socket.timedFillLineToSendExists(15 * time.Second) + } return nil } -// fillLineToSendExists only exists because you can't goroutine single statements. -func (socket *Socket) fillLineToSendExists(force bool) { - if force || !socket.Closed { - socket.lineToSendExists <- true +// timedFillLineToSendExists either sends the note or times out. +func (socket *Socket) timedFillLineToSendExists(duration time.Duration) { + select { + case socket.lineToSendExists <- true: + // passed data successfully + case <-time.After(duration): + // timed out send } } @@ -142,12 +149,13 @@ func (socket *Socket) RunSocketWriter() { // check if we're closed if socket.Closed { + socket.linesToSendMutex.Unlock() break } - // check number of lines to send + // check whether new lines actually exist or not if len(socket.linesToSend) < 1 { - fmt.Println("No line to send found on socket writer") + socket.linesToSendMutex.Unlock() continue } @@ -156,22 +164,19 @@ func (socket *Socket) RunSocketWriter() { for _, line := range socket.linesToSend { sendQBytes += uint64(len(line)) if socket.MaxSendQBytes < sendQBytes { + socket.linesToSendMutex.Unlock() break } } if socket.MaxSendQBytes < sendQBytes { - socket.conn.Write([]byte("\r\nERROR :SendQ Exceeded\r\n")) - fmt.Println("SendQ exceeded, disconnected client") + socket.FinalData = "\r\nERROR :SendQ Exceeded\r\n" + socket.linesToSendMutex.Unlock() break } - // get data - data := socket.linesToSend[0] - if len(socket.linesToSend) > 1 { - socket.linesToSend = socket.linesToSend[1:] - } else { - socket.linesToSend = []string{} - } + // get all existing data + data := strings.Join(socket.linesToSend, "") + socket.linesToSend = []string{} socket.linesToSendMutex.Unlock() @@ -190,10 +195,14 @@ func (socket *Socket) RunSocketWriter() { break } } - socket.conn.Close() if !socket.Closed { socket.Closed = true } + // write error lines + if 0 < len(socket.FinalData) { + socket.conn.Write([]byte(socket.FinalData)) + } + socket.conn.Close() // empty the lineToSendExists channel for 0 < len(socket.lineToSendExists) {