Close channel to signal goroutines to quit and waitgroup to confirm that they have

This commit is contained in:
alsm 2014-05-02 23:20:51 +01:00
parent 50d8ba24ee
commit f27b0b53e2
2 changed files with 28 additions and 40 deletions

51
irc.go
View File

@ -37,12 +37,12 @@ const (
// Read data from a connection. To be used as a goroutine. // Read data from a connection. To be used as a goroutine.
func (irc *Connection) readLoop() { func (irc *Connection) readLoop() {
defer irc.Done()
br := bufio.NewReaderSize(irc.socket, 512) br := bufio.NewReaderSize(irc.socket, 512)
for { for {
select { select {
case <-irc.endread: case <-irc.end:
irc.readerExit <- true
return return
default: default:
// Set a read deadline based on the combined timeout and ping frequency // Set a read deadline based on the combined timeout and ping frequency
@ -97,21 +97,19 @@ func (irc *Connection) readLoop() {
irc.RunCallbacks(event) irc.RunCallbacks(event)
} }
} }
return
irc.readerExit <- true
} }
// Loop to write to a connection. To be used as a goroutine. // Loop to write to a connection. To be used as a goroutine.
func (irc *Connection) writeLoop() { func (irc *Connection) writeLoop() {
defer irc.Done()
for { for {
select { select {
case <-irc.endwrite: case <-irc.end:
irc.writerExit <- true
return return
default: default:
b, ok := <-irc.pwrite b, ok := <-irc.pwrite
if !ok || b == "" || irc.socket == nil { if !ok || b == "" || irc.socket == nil {
irc.writerExit <- true
return return
} }
@ -130,17 +128,17 @@ func (irc *Connection) writeLoop() {
if err != nil { if err != nil {
irc.Error <- err irc.Error <- err
irc.writerExit <- true
return return
} }
} }
} }
irc.writerExit <- true return
} }
// Pings the server if we have not received any messages for 5 minutes // Pings the server if we have not received any messages for 5 minutes
// to keep the connection alive. To be used as a goroutine. // to keep the connection alive. To be used as a goroutine.
func (irc *Connection) pingLoop() { func (irc *Connection) pingLoop() {
defer irc.Done()
ticker := time.NewTicker(1 * time.Minute) // Tick every minute for monitoring ticker := time.NewTicker(1 * time.Minute) // Tick every minute for monitoring
ticker2 := time.NewTicker(irc.PingFreq) // Tick at the ping frequency. ticker2 := time.NewTicker(irc.PingFreq) // Tick at the ping frequency.
for { for {
@ -158,10 +156,9 @@ func (irc *Connection) pingLoop() {
irc.nickcurrent = irc.nick irc.nickcurrent = irc.nick
irc.SendRawf("NICK %s", irc.nick) irc.SendRawf("NICK %s", irc.nick)
} }
case <-irc.endping: case <-irc.end:
ticker.Stop() ticker.Stop()
ticker2.Stop() ticker2.Stop()
irc.pingerExit <- true
return return
} }
} }
@ -222,7 +219,7 @@ func (irc *Connection) Noticef(target, format string, a ...interface{}) {
// Send (action) message to a target (channel or nickname). // Send (action) message to a target (channel or nickname).
// No clear RFC on this one... // No clear RFC on this one...
func (irc *Connection) Action(target, message string) { func (irc *Connection) Action(target, message string) {
irc.pwrite <- fmt.Sprintf("PRIVMSG %s :\001ACTION %s\001\r\n", target, message) irc.pwrite <- fmt.Sprintf("PRIVMSG %s :\001ACTION %s\001\r\n", target, message)
} }
// Send (private) message to a target (channel or nickname). // Send (private) message to a target (channel or nickname).
@ -284,15 +281,11 @@ func (irc *Connection) Mode(target string, modestring ...string) {
// A disconnect sends all buffered messages (if possible), // A disconnect 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 close(irc.end)
irc.endwrite <- true
irc.endread <- true
close(irc.pwrite) close(irc.pwrite)
close(irc.pread) close(irc.pread)
<-irc.readerExit irc.Wait()
<-irc.writerExit
<-irc.pingerExit
irc.socket.Close() irc.socket.Close()
irc.socket = nil irc.socket = nil
if irc.netsock != nil { if irc.netsock != nil {
@ -362,6 +355,7 @@ func (irc *Connection) Connect(server string) error {
irc.pwrite = make(chan string, 10) irc.pwrite = make(chan string, 10)
irc.Error = make(chan error, 2) irc.Error = make(chan error, 2)
irc.Add(3)
go irc.readLoop() go irc.readLoop()
go irc.writeLoop() go irc.writeLoop()
go irc.pingLoop() go irc.pingLoop()
@ -387,19 +381,14 @@ func IRC(nick, user string) *Connection {
} }
irc := &Connection{ irc := &Connection{
nick: nick, nick: nick,
user: user, user: user,
Log: log.New(os.Stdout, "", log.LstdFlags), Log: log.New(os.Stdout, "", log.LstdFlags),
readerExit: make(chan bool), end: make(chan struct{}),
writerExit: make(chan bool), Version: VERSION,
pingerExit: make(chan bool), KeepAlive: 4 * time.Minute,
endping: make(chan bool), Timeout: 1 * time.Minute,
endread: make(chan bool), PingFreq: 15 * time.Minute,
endwrite: make(chan bool),
Version: VERSION,
KeepAlive: 4 * time.Minute,
Timeout: 1 * time.Minute,
PingFreq: 15 * time.Minute,
} }
irc.setupCallbacks() irc.setupCallbacks()
return irc return irc

View File

@ -8,10 +8,12 @@ import (
"crypto/tls" "crypto/tls"
"log" "log"
"net" "net"
"sync"
"time" "time"
) )
type Connection struct { type Connection struct {
sync.WaitGroup
Debug bool Debug bool
Error chan error Error chan error
Password string Password string
@ -22,11 +24,10 @@ type Connection struct {
PingFreq time.Duration PingFreq time.Duration
KeepAlive time.Duration KeepAlive time.Duration
socket net.Conn socket net.Conn
netsock net.Conn netsock net.Conn
pread, pwrite chan string pread, pwrite chan string
readerExit, writerExit, pingerExit chan bool end chan struct{}
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.
@ -43,7 +44,6 @@ type Connection struct {
stopped bool stopped bool
} }
// A struct to represent an event. // A struct to represent an event.
type Event struct { type Event struct {
Code string Code string
@ -55,9 +55,8 @@ type Event struct {
Arguments []string Arguments []string
} }
// Retrieve the last message from Event arguments.
// Retrieve the last message from Event arguments. // This function leaves the arguments untouched and
// This function leaves the arguments untouched and
// returns an empty string if there are none. // returns an empty string if there are none.
func (e *Event) Message() string { func (e *Event) Message() string {
if len(e.Arguments) == 0 { if len(e.Arguments) == 0 {