github.com/vipernet-xyz/tm@v0.34.24/p2p/peer_set.go (about)

     1  package p2p
     2  
     3  import (
     4  	"net"
     5  
     6  	tmsync "github.com/vipernet-xyz/tm/libs/sync"
     7  )
     8  
     9  // IPeerSet has a (immutable) subset of the methods of PeerSet.
    10  type IPeerSet interface {
    11  	Has(key ID) bool
    12  	HasIP(ip net.IP) bool
    13  	Get(key ID) Peer
    14  	List() []Peer
    15  	Size() int
    16  }
    17  
    18  //-----------------------------------------------------------------------------
    19  
    20  // PeerSet is a special structure for keeping a table of peers.
    21  // Iteration over the peers is super fast and thread-safe.
    22  type PeerSet struct {
    23  	mtx    tmsync.Mutex
    24  	lookup map[ID]*peerSetItem
    25  	list   []Peer
    26  }
    27  
    28  type peerSetItem struct {
    29  	peer  Peer
    30  	index int
    31  }
    32  
    33  // NewPeerSet creates a new peerSet with a list of initial capacity of 256 items.
    34  func NewPeerSet() *PeerSet {
    35  	return &PeerSet{
    36  		lookup: make(map[ID]*peerSetItem),
    37  		list:   make([]Peer, 0, 256),
    38  	}
    39  }
    40  
    41  // Add adds the peer to the PeerSet.
    42  // It returns an error carrying the reason, if the peer is already present.
    43  func (ps *PeerSet) Add(peer Peer) error {
    44  	ps.mtx.Lock()
    45  	defer ps.mtx.Unlock()
    46  
    47  	if ps.lookup[peer.ID()] != nil {
    48  		return ErrSwitchDuplicatePeerID{peer.ID()}
    49  	}
    50  	if peer.GetRemovalFailed() {
    51  		return ErrPeerRemoval{}
    52  	}
    53  
    54  	index := len(ps.list)
    55  	// Appending is safe even with other goroutines
    56  	// iterating over the ps.list slice.
    57  	ps.list = append(ps.list, peer)
    58  	ps.lookup[peer.ID()] = &peerSetItem{peer, index}
    59  	return nil
    60  }
    61  
    62  // Has returns true if the set contains the peer referred to by this
    63  // peerKey, otherwise false.
    64  func (ps *PeerSet) Has(peerKey ID) bool {
    65  	ps.mtx.Lock()
    66  	_, ok := ps.lookup[peerKey]
    67  	ps.mtx.Unlock()
    68  	return ok
    69  }
    70  
    71  // HasIP returns true if the set contains the peer referred to by this IP
    72  // address, otherwise false.
    73  func (ps *PeerSet) HasIP(peerIP net.IP) bool {
    74  	ps.mtx.Lock()
    75  	defer ps.mtx.Unlock()
    76  
    77  	return ps.hasIP(peerIP)
    78  }
    79  
    80  // hasIP does not acquire a lock so it can be used in public methods which
    81  // already lock.
    82  func (ps *PeerSet) hasIP(peerIP net.IP) bool {
    83  	for _, item := range ps.lookup {
    84  		if item.peer.RemoteIP().Equal(peerIP) {
    85  			return true
    86  		}
    87  	}
    88  
    89  	return false
    90  }
    91  
    92  // Get looks up a peer by the provided peerKey. Returns nil if peer is not
    93  // found.
    94  func (ps *PeerSet) Get(peerKey ID) Peer {
    95  	ps.mtx.Lock()
    96  	defer ps.mtx.Unlock()
    97  	item, ok := ps.lookup[peerKey]
    98  	if ok {
    99  		return item.peer
   100  	}
   101  	return nil
   102  }
   103  
   104  // Remove discards peer by its Key, if the peer was previously memoized.
   105  // Returns true if the peer was removed, and false if it was not found.
   106  // in the set.
   107  func (ps *PeerSet) Remove(peer Peer) bool {
   108  	ps.mtx.Lock()
   109  	defer ps.mtx.Unlock()
   110  
   111  	item := ps.lookup[peer.ID()]
   112  	if item == nil {
   113  		// Removing the peer has failed so we set a flag to mark that a removal was attempted.
   114  		// This can happen when the peer add routine from the switch is running in
   115  		// parallel to the receive routine of MConn.
   116  		// There is an error within MConn but the switch has not actually added the peer to the peer set yet.
   117  		// Setting this flag will prevent a peer from being added to a node's peer set afterwards.
   118  		peer.SetRemovalFailed()
   119  		return false
   120  	}
   121  
   122  	index := item.index
   123  	// Create a new copy of the list but with one less item.
   124  	// (we must copy because we'll be mutating the list).
   125  	newList := make([]Peer, len(ps.list)-1)
   126  	copy(newList, ps.list)
   127  	// If it's the last peer, that's an easy special case.
   128  	if index == len(ps.list)-1 {
   129  		ps.list = newList
   130  		delete(ps.lookup, peer.ID())
   131  		return true
   132  	}
   133  
   134  	// Replace the popped item with the last item in the old list.
   135  	lastPeer := ps.list[len(ps.list)-1]
   136  	lastPeerKey := lastPeer.ID()
   137  	lastPeerItem := ps.lookup[lastPeerKey]
   138  	newList[index] = lastPeer
   139  	lastPeerItem.index = index
   140  	ps.list = newList
   141  	delete(ps.lookup, peer.ID())
   142  	return true
   143  }
   144  
   145  // Size returns the number of unique items in the peerSet.
   146  func (ps *PeerSet) Size() int {
   147  	ps.mtx.Lock()
   148  	defer ps.mtx.Unlock()
   149  	return len(ps.list)
   150  }
   151  
   152  // List returns the threadsafe list of peers.
   153  func (ps *PeerSet) List() []Peer {
   154  	ps.mtx.Lock()
   155  	defer ps.mtx.Unlock()
   156  	return ps.list
   157  }