2020-01-15 04:43:34 +00:00
|
|
|
package gateway
|
|
|
|
|
|
|
|
import (
|
2020-01-29 03:54:22 +00:00
|
|
|
"sync/atomic"
|
2020-01-15 04:43:34 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
|
|
|
var ErrDead = errors.New("no heartbeat replied")
|
|
|
|
|
2020-01-29 03:54:22 +00:00
|
|
|
// Time is a UnixNano timestamp.
|
|
|
|
type Time = int64
|
|
|
|
|
2020-01-15 04:43:34 +00:00
|
|
|
type Pacemaker struct {
|
|
|
|
// Heartrate is the received duration between heartbeats.
|
|
|
|
Heartrate time.Duration
|
|
|
|
|
2020-01-29 03:54:22 +00:00
|
|
|
// Time in nanoseconds, guarded by atomic read/writes.
|
|
|
|
SentBeat Time
|
|
|
|
EchoBeat Time
|
2020-01-15 04:43:34 +00:00
|
|
|
|
|
|
|
// Any callback that returns an error will stop the pacer.
|
|
|
|
Pace func() error
|
|
|
|
// Event
|
|
|
|
OnDead func() error
|
|
|
|
|
2020-01-20 11:06:20 +00:00
|
|
|
stop chan<- struct{}
|
|
|
|
death chan error
|
2020-01-15 04:43:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Pacemaker) Echo() {
|
|
|
|
// Swap our received heartbeats
|
2020-01-19 06:22:03 +00:00
|
|
|
// p.LastBeat[0], p.LastBeat[1] = time.Now(), p.LastBeat[0]
|
2020-01-29 03:54:22 +00:00
|
|
|
atomic.StoreInt64(&p.EchoBeat, time.Now().UnixNano())
|
2020-01-15 04:43:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Dead, if true, will have Pace return an ErrDead.
|
|
|
|
func (p *Pacemaker) Dead() bool {
|
2020-01-19 06:22:03 +00:00
|
|
|
/* Deprecated
|
2020-01-15 04:43:34 +00:00
|
|
|
if p.LastBeat[0].IsZero() || p.LastBeat[1].IsZero() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return p.LastBeat[0].Sub(p.LastBeat[1]) > p.Heartrate*2
|
2020-01-19 06:22:03 +00:00
|
|
|
*/
|
|
|
|
|
2020-01-29 03:54:22 +00:00
|
|
|
var (
|
|
|
|
echo = atomic.LoadInt64(&p.EchoBeat)
|
|
|
|
sent = atomic.LoadInt64(&p.SentBeat)
|
|
|
|
)
|
|
|
|
|
|
|
|
if echo == 0 || sent == 0 {
|
2020-01-19 06:22:03 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-01-29 03:54:22 +00:00
|
|
|
return sent-echo > int64(p.Heartrate)*2
|
2020-01-15 04:43:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Pacemaker) Stop() {
|
2020-01-18 07:40:44 +00:00
|
|
|
if p.stop != nil {
|
|
|
|
close(p.stop)
|
2020-01-19 06:34:52 +00:00
|
|
|
p.stop = nil
|
2020-01-18 07:40:44 +00:00
|
|
|
}
|
2020-01-15 04:43:34 +00:00
|
|
|
}
|
|
|
|
|
2020-01-16 03:28:21 +00:00
|
|
|
func (p *Pacemaker) start(stop chan struct{}) error {
|
|
|
|
tick := time.NewTicker(p.Heartrate)
|
|
|
|
defer tick.Stop()
|
2020-01-15 04:43:34 +00:00
|
|
|
|
2020-01-16 03:28:21 +00:00
|
|
|
// Echo at least once
|
|
|
|
p.Echo()
|
|
|
|
|
|
|
|
for {
|
2020-01-15 04:43:34 +00:00
|
|
|
select {
|
|
|
|
case <-stop:
|
|
|
|
return nil
|
2020-01-18 07:40:44 +00:00
|
|
|
|
2020-01-15 04:43:34 +00:00
|
|
|
case <-tick.C:
|
2020-01-16 03:28:21 +00:00
|
|
|
if err := p.Pace(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-01-29 03:54:22 +00:00
|
|
|
// Paced, save:
|
|
|
|
atomic.StoreInt64(&p.SentBeat, time.Now().UnixNano())
|
2020-01-19 06:22:03 +00:00
|
|
|
|
2020-01-16 03:28:21 +00:00
|
|
|
if p.Dead() {
|
2020-01-20 09:32:28 +00:00
|
|
|
return ErrDead
|
2020-01-16 03:28:21 +00:00
|
|
|
}
|
2020-01-15 04:43:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-20 11:06:20 +00:00
|
|
|
func (p *Pacemaker) StartAsync() (death chan error) {
|
|
|
|
p.death = make(chan error)
|
2020-01-16 03:28:21 +00:00
|
|
|
|
|
|
|
stop := make(chan struct{})
|
|
|
|
p.stop = stop
|
|
|
|
|
2020-01-15 04:43:34 +00:00
|
|
|
go func() {
|
2020-01-20 11:06:20 +00:00
|
|
|
p.death <- p.start(stop)
|
2020-01-15 04:43:34 +00:00
|
|
|
}()
|
2020-01-16 03:28:21 +00:00
|
|
|
|
2020-01-20 11:06:20 +00:00
|
|
|
return p.death
|
2020-01-15 04:43:34 +00:00
|
|
|
}
|