2018-08-27 20:01:08 -07:00
|
|
|
package pubsub
|
2016-10-20 01:01:06 +02:00
|
|
|
|
2016-11-17 11:27:57 +01:00
|
|
|
import (
|
|
|
|
|
"context"
|
2019-06-07 08:31:14 -04:00
|
|
|
"github.com/libp2p/go-libp2p-core/peer"
|
2019-08-01 16:57:05 -04:00
|
|
|
"sync"
|
2016-11-17 11:27:57 +01:00
|
|
|
)
|
|
|
|
|
|
2019-07-22 10:02:58 -04:00
|
|
|
type EventType int
|
2019-06-12 10:06:16 -04:00
|
|
|
|
|
|
|
|
const (
|
2019-07-22 10:02:58 -04:00
|
|
|
PEER_JOIN EventType = iota
|
2019-06-12 10:06:16 -04:00
|
|
|
PEER_LEAVE
|
|
|
|
|
)
|
|
|
|
|
|
2016-10-20 01:01:06 +02:00
|
|
|
type Subscription struct {
|
2019-06-12 10:06:16 -04:00
|
|
|
topic string
|
|
|
|
|
ch chan *Message
|
|
|
|
|
cancelCh chan<- *Subscription
|
|
|
|
|
err error
|
2019-08-01 16:57:05 -04:00
|
|
|
|
|
|
|
|
peerEvtCh chan PeerEvent
|
|
|
|
|
eventMx sync.Mutex
|
|
|
|
|
evtBacklog map[peer.ID]EventType
|
|
|
|
|
backlogCh chan PeerEvent
|
2019-06-12 10:06:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type PeerEvent struct {
|
|
|
|
|
Type EventType
|
|
|
|
|
Peer peer.ID
|
2016-10-20 01:01:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (sub *Subscription) Topic() string {
|
|
|
|
|
return sub.topic
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-12 10:06:16 -04:00
|
|
|
// Next returns the next message in our subscription
|
2016-11-17 11:27:57 +01:00
|
|
|
func (sub *Subscription) Next(ctx context.Context) (*Message, error) {
|
|
|
|
|
select {
|
|
|
|
|
case msg, ok := <-sub.ch:
|
|
|
|
|
if !ok {
|
|
|
|
|
return msg, sub.err
|
|
|
|
|
}
|
2016-10-20 01:01:06 +02:00
|
|
|
|
2016-11-17 11:27:57 +01:00
|
|
|
return msg, nil
|
|
|
|
|
case <-ctx.Done():
|
|
|
|
|
return nil, ctx.Err()
|
2016-10-20 01:01:06 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (sub *Subscription) Cancel() {
|
|
|
|
|
sub.cancelCh <- sub
|
|
|
|
|
}
|
2019-06-07 08:31:14 -04:00
|
|
|
|
2019-07-01 17:43:49 +02:00
|
|
|
func (sub *Subscription) close() {
|
2019-06-21 08:46:41 +02:00
|
|
|
close(sub.ch)
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-01 16:57:05 -04:00
|
|
|
func (sub *Subscription) sendNotification(evt PeerEvent) {
|
|
|
|
|
sub.eventMx.Lock()
|
|
|
|
|
defer sub.eventMx.Unlock()
|
|
|
|
|
|
|
|
|
|
e, ok := sub.evtBacklog[evt.Peer]
|
|
|
|
|
if ok && e != evt.Type {
|
|
|
|
|
delete(sub.evtBacklog, evt.Peer)
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-07 08:31:14 -04:00
|
|
|
select {
|
2019-08-01 16:57:05 -04:00
|
|
|
case sub.peerEvtCh <- evt:
|
|
|
|
|
default:
|
|
|
|
|
// Empty event queue into backlog
|
|
|
|
|
emptyqueue:
|
|
|
|
|
for {
|
|
|
|
|
select {
|
|
|
|
|
case e := <-sub.peerEvtCh:
|
|
|
|
|
sub.addToBacklog(e)
|
|
|
|
|
default:
|
|
|
|
|
break emptyqueue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
sub.addToBacklog(evt)
|
|
|
|
|
if e, ok := sub.pullFromBacklog(); ok {
|
|
|
|
|
sub.peerEvtCh <- e
|
2019-06-07 08:31:14 -04:00
|
|
|
}
|
2019-08-01 16:57:05 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// addToBacklog assumes a lock has been taken to protect the backlog
|
|
|
|
|
func (sub *Subscription) addToBacklog(evt PeerEvent) {
|
|
|
|
|
e, ok := sub.evtBacklog[evt.Peer]
|
|
|
|
|
if !ok {
|
|
|
|
|
sub.evtBacklog[evt.Peer] = evt.Type
|
|
|
|
|
} else if e != evt.Type {
|
|
|
|
|
delete(sub.evtBacklog, evt.Peer)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// pullFromBacklog assumes a lock has been taken to protect the backlog
|
|
|
|
|
func (sub *Subscription) pullFromBacklog() (PeerEvent, bool) {
|
|
|
|
|
for k, v := range sub.evtBacklog {
|
|
|
|
|
evt := PeerEvent{Peer: k, Type: v}
|
|
|
|
|
delete(sub.evtBacklog, k)
|
|
|
|
|
return evt, true
|
|
|
|
|
}
|
|
|
|
|
return PeerEvent{}, false
|
|
|
|
|
}
|
2019-06-07 08:31:14 -04:00
|
|
|
|
2019-08-01 16:57:05 -04:00
|
|
|
// NextPeerEvent returns the next event regarding subscribed peers
|
|
|
|
|
// Guarantees: Peer Join and Peer Leave events for a given peer will fire in order.
|
|
|
|
|
// Unless a peer both Joins and Leaves before NextPeerEvent emits either event
|
|
|
|
|
// all events will eventually be received from NextPeerEvent.
|
|
|
|
|
func (sub *Subscription) NextPeerEvent(ctx context.Context) (PeerEvent, error) {
|
|
|
|
|
sub.eventMx.Lock()
|
|
|
|
|
evt, ok := sub.pullFromBacklog()
|
|
|
|
|
sub.eventMx.Unlock()
|
|
|
|
|
|
|
|
|
|
if ok {
|
|
|
|
|
return evt, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
select {
|
|
|
|
|
case evt, ok := <-sub.peerEvtCh:
|
2019-06-11 17:49:28 -04:00
|
|
|
if !ok {
|
2019-08-01 16:57:05 -04:00
|
|
|
return PeerEvent{}, sub.err
|
2019-06-11 17:49:28 -04:00
|
|
|
}
|
|
|
|
|
|
2019-08-01 16:57:05 -04:00
|
|
|
return evt, nil
|
2019-06-11 17:49:28 -04:00
|
|
|
case <-ctx.Done():
|
2019-06-12 10:06:16 -04:00
|
|
|
return PeerEvent{}, ctx.Err()
|
2019-06-11 17:49:28 -04:00
|
|
|
}
|
|
|
|
|
}
|