go-gun/gun/gun_peer.go
2019-02-22 13:51:50 -06:00

109 lines
2.3 KiB
Go

package gun
import (
"context"
"sync"
"time"
)
type gunPeer struct {
url string
connPeer func() (Peer, error)
sleepOnErr time.Duration // TODO: would be better as backoff
id string
peer Peer
peerBad bool // If true, don't try anything
peerLock sync.Mutex
}
func newGunPeer(url string, connPeer func() (Peer, error), sleepOnErr time.Duration) (*gunPeer, error) {
p := &gunPeer{url: url, connPeer: connPeer, sleepOnErr: sleepOnErr}
var err error
if p.peer, err = connPeer(); err != nil {
return nil, err
}
return p, nil
}
func (g *gunPeer) ID() string { return g.id }
func (g *gunPeer) reconnectPeer() (err error) {
g.peerLock.Lock()
defer g.peerLock.Unlock()
if g.peer == nil && g.peerBad {
g.peerBad = false
if g.peer, err = g.connPeer(); err != nil {
g.peerBad = true
time.AfterFunc(g.sleepOnErr, func() { g.reconnectPeer() })
}
}
return
}
// Can be nil peer if currently bad
func (g *gunPeer) connectedPeer() Peer {
g.peerLock.Lock()
defer g.peerLock.Unlock()
return g.peer
}
func (g *gunPeer) markPeerErrored(p Peer) {
g.peerLock.Lock()
defer g.peerLock.Unlock()
if p == g.peer {
g.peer = nil
g.peerBad = true
p.Close()
time.AfterFunc(g.sleepOnErr, func() { g.reconnectPeer() })
}
}
func (g *gunPeer) send(ctx context.Context, msg *Message, moreMsgs ...*Message) (ok bool, err error) {
p := g.connectedPeer()
if p == nil {
return false, nil
}
// Clone them with peer "to"
updatedMsg := msg.Clone()
updatedMsg.To = g.url
updatedMoreMsgs := make([]*Message, len(moreMsgs))
for i, moreMsg := range moreMsgs {
moreMsg := moreMsg.Clone()
moreMsg.To = g.url
updatedMoreMsgs[i] = moreMsg
}
if err = p.Send(ctx, updatedMsg, updatedMoreMsgs...); err != nil {
g.markPeerErrored(p)
return false, err
} else {
return true, nil
}
}
func (g *gunPeer) receive(ctx context.Context) (ok bool, msgs []*Message, err error) {
if p := g.connectedPeer(); p == nil {
return false, nil, nil
} else if msgs, err = p.Receive(ctx); err != nil {
g.markPeerErrored(p)
return false, nil, err
} else {
return true, msgs, nil
}
}
func (g *gunPeer) Close() error {
g.peerLock.Lock()
defer g.peerLock.Unlock()
err := g.peer.Close()
g.peer = nil
g.peerBad = false
return err
}
func (g *gunPeer) closed() bool {
g.peerLock.Lock()
defer g.peerLock.Unlock()
return g.peer == nil && !g.peerBad
}