1
0
Fork 0
mirror of https://github.com/diamondburned/arikawa.git synced 2024-11-18 04:42:54 +00:00
arikawa/gateway/pacemaker.go

96 lines
1.6 KiB
Go
Raw Normal View History

2020-01-15 04:43:34 +00:00
package gateway
import (
"time"
"github.com/pkg/errors"
)
var ErrDead = errors.New("no heartbeat replied")
type Pacemaker struct {
// Heartrate is the received duration between heartbeats.
Heartrate time.Duration
// LastBeat logs the received heartbeats, with the newest one
// first.
LastBeat [2]time.Time
// Any callback that returns an error will stop the pacer.
Pace func() error
// Event
OnDead func() error
stop chan<- struct{}
}
func (p *Pacemaker) Echo() {
// Swap our received heartbeats
p.LastBeat[0], p.LastBeat[1] = time.Now(), p.LastBeat[0]
}
// Dead, if true, will have Pace return an ErrDead.
func (p *Pacemaker) Dead() bool {
if p.LastBeat[0].IsZero() || p.LastBeat[1].IsZero() {
return false
}
return p.LastBeat[0].Sub(p.LastBeat[1]) > p.Heartrate*2
}
func (p *Pacemaker) Stop() {
2020-01-18 07:40:44 +00:00
if p.stop != nil {
close(p.stop)
p.stop = nil
}
2020-01-15 04:43:34 +00:00
}
// Start beats until it's dead.
func (p *Pacemaker) Start() error {
stop := make(chan struct{})
p.stop = stop
2020-01-16 03:28:21 +00:00
return p.start(stop)
}
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()
2020-01-18 07:40:44 +00:00
// TODO: what happens if the heartbeat fails?
2020-01-16 03:28:21 +00:00
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
}
if p.Dead() {
if err := p.OnDead(); err != nil {
return err
}
}
2020-01-15 04:43:34 +00:00
}
}
}
func (p *Pacemaker) StartAsync() (death <-chan error) {
var ch = 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-16 03:28:21 +00:00
ch <- p.start(stop)
2020-01-15 04:43:34 +00:00
}()
2020-01-16 03:28:21 +00:00
2020-01-15 04:43:34 +00:00
return ch
}