From e0b46a8cff40e9a6d965a18900572c4832e1d075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Janouch?=
Date: Wed, 1 Aug 2018 17:49:27 +0200 Subject: [PATCH] hid: figured out how to port timeouts --- hid/main.go | 84 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 64 insertions(+), 20 deletions(-) diff --git a/hid/main.go b/hid/main.go index bcf7bc6..c3b2875 100644 --- a/hid/main.go +++ b/hid/main.go @@ -761,7 +761,7 @@ var ( config simpleConfig // server configuration serverName string // our server name - pingInterval uint // ping interval in seconds + pingInterval time.Duration // ping interval maxConnections int // max connections allowed or 0 motd []string // MOTD (none if empty) operators = make(map[string]bool) // TLS cert. fingerprints for IRCops @@ -773,7 +773,7 @@ var ( prepared = make(chan preparedEvent) reads = make(chan readEvent) writes = make(chan writeEvent) - timeouts = make(chan *client) + timers = make(chan func()) tlsConf *tls.Config clients = make(map[*client]bool) @@ -962,12 +962,7 @@ func (c *client) kill(reason string) { _ = c.transport.Close() } - // Clean up the goroutine, although a spurious event may still be sent. - // TODO: Other timers if needed. - if c.killTimer != nil { - c.killTimer.Stop() - } - + c.cancelTimers() delete(clients, c) } @@ -994,9 +989,7 @@ func (c *client) closeLink(reason string) { c.closing = true c.unregister(reason) - c.killTimer = time.AfterFunc(3*time.Second, func() { - timeouts <- c - }) + c.setKillTimer() } func (c *client) inMaskList(masks []string) bool { @@ -1025,7 +1018,57 @@ func (c *client) getTLSCertFingerprint() string { // --- Timers ------------------------------------------------------------------ -// TODO +// Free the resources of timers that haven't fired yet and for timers that are +// in between firing and being collected by the event loop, mark that the event +// should not be acted upon. +func (c *client) cancelTimers() { + for _, timer := range []**time.Timer{ + &c.killTimer, &c.timeoutTimer, &c.pingTimer, + } { + if *timer != nil { + (*timer).Stop() + *timer = nil + } + } +} + +// Arrange for a function to be called later from the main goroutine. +func (c *client) setTimer(timer **time.Timer, delay time.Duration, cb func()) { + c.cancelTimers() + + var identityCapture *time.Timer + identityCapture = time.AfterFunc(delay, func() { + timers <- func() { + // The timer might have been cancelled or even replaced. + // When the client is killed, this will be nil. + if *timer == identityCapture { + cb() + } + } + }) + + *timer = identityCapture +} + +func (c *client) setKillTimer() { + c.setTimer(&c.killTimer, pingInterval, func() { + c.kill("Timeout") + }) +} + +func (c *client) setTimeoutTimer() { + c.setTimer(&c.timeoutTimer, pingInterval, func() { + c.closeLink(fmt.Sprintf("Ping timeout: >%d seconds", + pingInterval/time.Second)) + }) +} + +func (c *client) setPingTimer() { + c.setTimer(&c.pingTimer, pingInterval, func() { + c.sendf("PING :%s", serverName) + c.setTimeoutTimer() + }) +} // --- IRC command handling ---------------------------------------------------- @@ -1451,7 +1494,7 @@ func ircHandlePONG(msg *message, c *client) { } // Set a new timer to send another PING - // TODO + c.setPingTimer() } func ircHandleQUIT(msg *message, c *client) { @@ -2817,6 +2860,7 @@ func (c *client) onPrepared(host string, isTLS bool) { // If we tried to send any data before now, we would need to flushSendQ. go read(c) c.reading = true + c.setPingTimer() } // Handle the results from trying to read from the client connection. @@ -2996,6 +3040,9 @@ func processOneEvent() { case <-quitTimer: forceQuit("timeout") + case callback := <-timers: + callback() + case conn := <-conns: if maxConnections > 0 && len(clients) >= maxConnections { log.Println("connection limit reached, refusing connection") @@ -3026,6 +3073,9 @@ func processOneEvent() { clients[c] = true go prepare(c) + // The TLS autodetection in prepare needs to have a timeout. + c.setKillTimer() + case ev := <-prepared: log.Println("client is ready:", ev.host) if _, ok := clients[ev.client]; ok { @@ -3043,12 +3093,6 @@ func processOneEvent() { if _, ok := clients[ev.client]; ok { ev.client.onWrite(ev.written, ev.err) } - - case c := <-timeouts: - if _, ok := clients[c]; ok { - log.Println("client timeouted") - c.kill("TODO") - } } } @@ -3147,7 +3191,7 @@ func ircParseConfig() error { } else if u < 1 { return "the value is out of range" } else { - pingInterval = uint(u) + pingInterval = time.Second * time.Duration(u) } return "" })