
This commit includes updates that affects the build, testing, and deployment of Tile38. - The root level build.sh has been broken up into multiple scripts and placed in the "scripts" directory. - The vendor directory has been updated to follow the Go modules rules, thus `make` should work on isolated environments. Also some vendored packages may have been updated to a later version, if needed. - The Makefile has been updated to allow for making single binaries such as `make tile38-server`. There is some scaffolding during the build process, so from now on all binaries should be made using make. For example, to run a development version of the tile38-cli binary, do this: make tile38-cli && ./tile38-cli not this: go run cmd/tile38-cli/main.go - Travis.CI docker push script has been updated to address a change to Docker's JSON repo meta output, which in turn fixes a bug where new Tile38 versions were not being properly pushed to Docker
164 lines
3.2 KiB
Go
164 lines
3.2 KiB
Go
/*
|
|
* Copyright (c) 2013 IBM Corp.
|
|
*
|
|
* All rights reserved. This program and the accompanying materials
|
|
* are made available under the terms of the Eclipse Public License v1.0
|
|
* which accompanies this distribution, and is available at
|
|
* http://www.eclipse.org/legal/epl-v10.html
|
|
*
|
|
* Contributors:
|
|
* Seth Hoenig
|
|
* Allan Stockdill-Mander
|
|
* Mike Robertson
|
|
*/
|
|
|
|
package mqtt
|
|
|
|
import (
|
|
"errors"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/eclipse/paho.mqtt.golang/packets"
|
|
)
|
|
|
|
func keepalive(c *client) {
|
|
DEBUG.Println(PNG, "keepalive starting")
|
|
|
|
var condWG sync.WaitGroup
|
|
pingStop := make(chan struct{})
|
|
|
|
defer func() {
|
|
close(pingStop)
|
|
c.keepaliveReset.Broadcast()
|
|
c.pingResp.Broadcast()
|
|
c.packetResp.Broadcast()
|
|
condWG.Wait()
|
|
c.workers.Done()
|
|
}()
|
|
|
|
receiveInterval := c.options.KeepAlive + (1 * time.Second)
|
|
pingTimer := timer{Timer: time.NewTimer(c.options.KeepAlive)}
|
|
receiveTimer := timer{Timer: time.NewTimer(receiveInterval)}
|
|
pingRespTimer := timer{Timer: time.NewTimer(c.options.PingTimeout)}
|
|
|
|
pingRespTimer.Stop()
|
|
|
|
condWG.Add(3)
|
|
go func() {
|
|
defer condWG.Done()
|
|
for {
|
|
c.pingResp.L.Lock()
|
|
c.pingResp.Wait()
|
|
c.pingResp.L.Unlock()
|
|
select {
|
|
case <-pingStop:
|
|
return
|
|
default:
|
|
}
|
|
DEBUG.Println(NET, "resetting ping timeout timer")
|
|
pingRespTimer.Stop()
|
|
pingTimer.Reset(c.options.KeepAlive)
|
|
receiveTimer.Reset(receiveInterval)
|
|
}
|
|
}()
|
|
|
|
go func() {
|
|
defer condWG.Done()
|
|
for {
|
|
c.packetResp.L.Lock()
|
|
c.packetResp.Wait()
|
|
c.packetResp.L.Unlock()
|
|
select {
|
|
case <-pingStop:
|
|
return
|
|
default:
|
|
}
|
|
DEBUG.Println(NET, "resetting receive timer")
|
|
receiveTimer.Reset(receiveInterval)
|
|
}
|
|
}()
|
|
|
|
go func() {
|
|
defer condWG.Done()
|
|
for {
|
|
c.keepaliveReset.L.Lock()
|
|
c.keepaliveReset.Wait()
|
|
c.keepaliveReset.L.Unlock()
|
|
select {
|
|
case <-pingStop:
|
|
return
|
|
default:
|
|
}
|
|
DEBUG.Println(NET, "resetting ping timer")
|
|
pingTimer.Reset(c.options.KeepAlive)
|
|
}
|
|
}()
|
|
|
|
for {
|
|
select {
|
|
case <-c.stop:
|
|
DEBUG.Println(PNG, "keepalive stopped")
|
|
return
|
|
case <-pingTimer.C:
|
|
sendPing(&pingTimer, &pingRespTimer, c)
|
|
case <-receiveTimer.C:
|
|
receiveTimer.SetRead(true)
|
|
receiveTimer.Reset(receiveInterval)
|
|
sendPing(&pingTimer, &pingRespTimer, c)
|
|
case <-pingRespTimer.C:
|
|
pingRespTimer.SetRead(true)
|
|
CRITICAL.Println(PNG, "pingresp not received, disconnecting")
|
|
c.errors <- errors.New("pingresp not received, disconnecting")
|
|
pingTimer.Stop()
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
type timer struct {
|
|
sync.Mutex
|
|
*time.Timer
|
|
readFrom bool
|
|
}
|
|
|
|
func (t *timer) SetRead(v bool) {
|
|
t.Lock()
|
|
t.readFrom = v
|
|
t.Unlock()
|
|
}
|
|
|
|
func (t *timer) Stop() bool {
|
|
t.Lock()
|
|
defer t.SetRead(true)
|
|
defer t.Unlock()
|
|
|
|
if !t.Timer.Stop() && !t.readFrom {
|
|
<-t.C
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (t *timer) Reset(d time.Duration) bool {
|
|
t.Lock()
|
|
defer t.SetRead(false)
|
|
defer t.Unlock()
|
|
if !t.Timer.Stop() && !t.readFrom {
|
|
<-t.C
|
|
}
|
|
|
|
return t.Timer.Reset(d)
|
|
}
|
|
|
|
func sendPing(pt *timer, rt *timer, c *client) {
|
|
pt.SetRead(true)
|
|
DEBUG.Println(PNG, "keepalive sending ping")
|
|
ping := packets.NewControlPacket(packets.Pingreq).(*packets.PingreqPacket)
|
|
//We don't want to wait behind large messages being sent, the Write call
|
|
//will block until it it able to send the packet.
|
|
ping.Write(c.conn)
|
|
|
|
rt.Reset(c.options.PingTimeout)
|
|
}
|