You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

121 lines
2.8 KiB

9 years ago
9 years ago
7 years ago
9 years ago
7 years ago
9 years ago
7 years ago
9 years ago
9 years ago
7 years ago
9 years ago
7 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
7 years ago
9 years ago
9 years ago
7 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
7 years ago
9 years ago
  1. package p2p
  2. import (
  3. "sync"
  4. "github.com/tendermint/tendermint/p2p/types"
  5. )
  6. // IPeerSet has a (immutable) subset of the methods of PeerSet.
  7. type IPeerSet interface {
  8. Has(key types.ID) bool
  9. Get(key types.ID) Peer
  10. List() []Peer
  11. Size() int
  12. }
  13. //-----------------------------------------------------------------------------
  14. // PeerSet is a special structure for keeping a table of peers.
  15. // Iteration over the peers is super fast and thread-safe.
  16. type PeerSet struct {
  17. mtx sync.Mutex
  18. lookup map[types.ID]*peerSetItem
  19. list []Peer
  20. }
  21. type peerSetItem struct {
  22. peer Peer
  23. index int
  24. }
  25. // NewPeerSet creates a new peerSet with a list of initial capacity of 256 items.
  26. func NewPeerSet() *PeerSet {
  27. return &PeerSet{
  28. lookup: make(map[types.ID]*peerSetItem),
  29. list: make([]Peer, 0, 256),
  30. }
  31. }
  32. // Add adds the peer to the PeerSet.
  33. // It returns ErrSwitchDuplicatePeer if the peer is already present.
  34. func (ps *PeerSet) Add(peer Peer) error {
  35. ps.mtx.Lock()
  36. defer ps.mtx.Unlock()
  37. if ps.lookup[peer.ID()] != nil {
  38. return types.ErrSwitchDuplicatePeer
  39. }
  40. index := len(ps.list)
  41. // Appending is safe even with other goroutines
  42. // iterating over the ps.list slice.
  43. ps.list = append(ps.list, peer)
  44. ps.lookup[peer.ID()] = &peerSetItem{peer, index}
  45. return nil
  46. }
  47. // Has returns true iff the PeerSet contains
  48. // the peer referred to by this peerKey.
  49. func (ps *PeerSet) Has(peerKey types.ID) bool {
  50. ps.mtx.Lock()
  51. _, ok := ps.lookup[peerKey]
  52. ps.mtx.Unlock()
  53. return ok
  54. }
  55. // Get looks up a peer by the provided peerKey.
  56. func (ps *PeerSet) Get(peerKey types.ID) Peer {
  57. ps.mtx.Lock()
  58. defer ps.mtx.Unlock()
  59. item, ok := ps.lookup[peerKey]
  60. if ok {
  61. return item.peer
  62. } else {
  63. return nil
  64. }
  65. }
  66. // Remove discards peer by its Key, if the peer was previously memoized.
  67. func (ps *PeerSet) Remove(peer Peer) {
  68. ps.mtx.Lock()
  69. defer ps.mtx.Unlock()
  70. item := ps.lookup[peer.ID()]
  71. if item == nil {
  72. return
  73. }
  74. index := item.index
  75. // Create a new copy of the list but with one less item.
  76. // (we must copy because we'll be mutating the list).
  77. newList := make([]Peer, len(ps.list)-1)
  78. copy(newList, ps.list)
  79. // If it's the last peer, that's an easy special case.
  80. if index == len(ps.list)-1 {
  81. ps.list = newList
  82. delete(ps.lookup, peer.ID())
  83. return
  84. }
  85. // Replace the popped item with the last item in the old list.
  86. lastPeer := ps.list[len(ps.list)-1]
  87. lastPeerKey := lastPeer.ID()
  88. lastPeerItem := ps.lookup[lastPeerKey]
  89. newList[index] = lastPeer
  90. lastPeerItem.index = index
  91. ps.list = newList
  92. delete(ps.lookup, peer.ID())
  93. }
  94. // Size returns the number of unique items in the peerSet.
  95. func (ps *PeerSet) Size() int {
  96. ps.mtx.Lock()
  97. defer ps.mtx.Unlock()
  98. return len(ps.list)
  99. }
  100. // List returns the threadsafe list of peers.
  101. func (ps *PeerSet) List() []Peer {
  102. ps.mtx.Lock()
  103. defer ps.mtx.Unlock()
  104. return ps.list
  105. }