171 lines
4.3 KiB
Go
171 lines
4.3 KiB
Go
package mesh
|
|
|
|
import (
|
|
"fmt"
|
|
)
|
|
|
|
type GossipData interface {
|
|
Encode() [][]byte
|
|
Merge(GossipData) GossipData
|
|
}
|
|
|
|
type Gossip interface {
|
|
// specific message from one peer to another
|
|
// intermediate peers relay it using unicast topology.
|
|
GossipUnicast(dstPeerName PeerName, msg []byte) error
|
|
// send gossip to every peer, relayed using broadcast topology.
|
|
GossipBroadcast(update GossipData) error
|
|
}
|
|
|
|
type Gossiper interface {
|
|
OnGossipUnicast(sender PeerName, msg []byte) error
|
|
// merge received data into state and return a representation of
|
|
// the received data, for further propagation
|
|
OnGossipBroadcast(sender PeerName, update []byte) (GossipData, error)
|
|
// return state of everything we know; gets called periodically
|
|
Gossip() GossipData
|
|
// merge received data into state and return "everything new I've
|
|
// just learnt", or nil if nothing in the received data was new
|
|
OnGossip(update []byte) (GossipData, error)
|
|
}
|
|
|
|
// Accumulates GossipData that needs to be sent to one destination,
|
|
// and sends it when possible.
|
|
type GossipSender struct {
|
|
send func(GossipData)
|
|
cell chan GossipData
|
|
// for testing
|
|
sent bool
|
|
flushch chan chan bool
|
|
}
|
|
|
|
func NewGossipSender(send func(GossipData)) *GossipSender {
|
|
cell := make(chan GossipData, 1)
|
|
flushch := make(chan chan bool)
|
|
sender := &GossipSender{
|
|
send: send,
|
|
cell: cell,
|
|
flushch: flushch,
|
|
}
|
|
go sender.run()
|
|
return sender
|
|
}
|
|
|
|
func (sender *GossipSender) run() {
|
|
for {
|
|
select {
|
|
case pending := <-sender.cell:
|
|
if pending == nil { // receive zero value when chan is closed
|
|
return
|
|
}
|
|
sender.send(pending)
|
|
sender.sent = true
|
|
case ch := <-sender.flushch:
|
|
// send anything pending, then reply back whether we sent
|
|
// anything since previous flush
|
|
select {
|
|
case pending := <-sender.cell:
|
|
sender.send(pending)
|
|
sender.sent = true
|
|
default:
|
|
}
|
|
ch <- sender.sent
|
|
sender.sent = false
|
|
}
|
|
}
|
|
}
|
|
|
|
func (sender *GossipSender) Send(data GossipData) {
|
|
// NB: this must not be invoked concurrently
|
|
select {
|
|
case pending := <-sender.cell:
|
|
sender.cell <- pending.Merge(data)
|
|
default:
|
|
sender.cell <- data
|
|
}
|
|
}
|
|
|
|
func (sender *GossipSender) Stop() {
|
|
close(sender.cell)
|
|
}
|
|
|
|
type GossipChannels map[string]*GossipChannel
|
|
|
|
func (router *Router) NewGossip(channelName string, g Gossiper) Gossip {
|
|
channel := NewGossipChannel(channelName, router.Ourself, router.Routes, g)
|
|
router.gossipLock.Lock()
|
|
defer router.gossipLock.Unlock()
|
|
if _, found := router.gossipChannels[channelName]; found {
|
|
checkFatal(fmt.Errorf("[gossip] duplicate channel %s", channelName))
|
|
}
|
|
router.gossipChannels[channelName] = channel
|
|
return channel
|
|
}
|
|
|
|
func (router *Router) gossipChannel(channelName string) *GossipChannel {
|
|
router.gossipLock.RLock()
|
|
channel, found := router.gossipChannels[channelName]
|
|
router.gossipLock.RUnlock()
|
|
if found {
|
|
return channel
|
|
}
|
|
router.gossipLock.Lock()
|
|
defer router.gossipLock.Unlock()
|
|
if channel, found = router.gossipChannels[channelName]; found {
|
|
return channel
|
|
}
|
|
channel = NewGossipChannel(channelName, router.Ourself, router.Routes, &surrogateGossiper)
|
|
channel.log("created surrogate channel")
|
|
router.gossipChannels[channelName] = channel
|
|
return channel
|
|
}
|
|
|
|
func (router *Router) gossipChannelSet() map[*GossipChannel]struct{} {
|
|
channels := make(map[*GossipChannel]struct{})
|
|
router.gossipLock.RLock()
|
|
defer router.gossipLock.RUnlock()
|
|
for _, channel := range router.gossipChannels {
|
|
channels[channel] = void
|
|
}
|
|
return channels
|
|
}
|
|
|
|
func (router *Router) SendAllGossip() {
|
|
for channel := range router.gossipChannelSet() {
|
|
if gossip := channel.gossiper.Gossip(); gossip != nil {
|
|
channel.Send(router.Ourself.Name, gossip)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (router *Router) SendAllGossipDown(conn Connection) {
|
|
for channel := range router.gossipChannelSet() {
|
|
if gossip := channel.gossiper.Gossip(); gossip != nil {
|
|
channel.SendDown(conn, gossip)
|
|
}
|
|
}
|
|
}
|
|
|
|
// for testing
|
|
|
|
func (router *Router) sendPendingGossip() bool {
|
|
sentSomething := false
|
|
for channel := range router.gossipChannelSet() {
|
|
channel.Lock()
|
|
for _, sender := range channel.senders {
|
|
sentSomething = sender.flush() || sentSomething
|
|
}
|
|
for _, sender := range channel.broadcasters {
|
|
sentSomething = sender.flush() || sentSomething
|
|
}
|
|
channel.Unlock()
|
|
}
|
|
return sentSomething
|
|
}
|
|
|
|
func (sender *GossipSender) flush() bool {
|
|
ch := make(chan bool)
|
|
sender.flushch <- ch
|
|
return <-ch
|
|
}
|